github.com/alanchchen/go-ethereum@v1.6.6-0.20170601190819-6171d01b1195/cmd/puppeth/wizard.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "bufio" 21 "encoding/json" 22 "fmt" 23 "io/ioutil" 24 "math/big" 25 "os" 26 "path/filepath" 27 "sort" 28 "strconv" 29 "strings" 30 "syscall" 31 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/core" 34 "github.com/ethereum/go-ethereum/log" 35 "golang.org/x/crypto/ssh/terminal" 36 ) 37 38 // config contains all the configurations needed by puppeth that should be saved 39 // between sessions. 40 type config struct { 41 path string // File containing the configuration values 42 genesis *core.Genesis // Genesis block to cache for node deploys 43 bootFull []string // Bootnodes to always connect to by full nodes 44 bootLight []string // Bootnodes to always connect to by light nodes 45 ethstats string // Ethstats settings to cache for node deploys 46 47 Servers map[string][]byte `json:"servers,omitempty"` 48 } 49 50 // servers retrieves an alphabetically sorted list of servers. 51 func (c config) servers() []string { 52 servers := make([]string, 0, len(c.Servers)) 53 for server := range c.Servers { 54 servers = append(servers, server) 55 } 56 sort.Strings(servers) 57 58 return servers 59 } 60 61 // flush dumps the contents of config to disk. 62 func (c config) flush() { 63 os.MkdirAll(filepath.Dir(c.path), 0755) 64 65 out, _ := json.MarshalIndent(c, "", " ") 66 if err := ioutil.WriteFile(c.path, out, 0644); err != nil { 67 log.Warn("Failed to save puppeth configs", "file", c.path, "err", err) 68 } 69 } 70 71 type wizard struct { 72 network string // Network name to manage 73 conf config // Configurations from previous runs 74 75 servers map[string]*sshClient // SSH connections to servers to administer 76 services map[string][]string // Ethereum services known to be running on servers 77 78 in *bufio.Reader // Wrapper around stdin to allow reading user input 79 } 80 81 // read reads a single line from stdin, trimming if from spaces. 82 func (w *wizard) read() string { 83 fmt.Printf("> ") 84 text, err := w.in.ReadString('\n') 85 if err != nil { 86 log.Crit("Failed to read user input", "err", err) 87 } 88 return strings.TrimSpace(text) 89 } 90 91 // readString reads a single line from stdin, trimming if from spaces, enforcing 92 // non-emptyness. 93 func (w *wizard) readString() string { 94 for { 95 fmt.Printf("> ") 96 text, err := w.in.ReadString('\n') 97 if err != nil { 98 log.Crit("Failed to read user input", "err", err) 99 } 100 if text = strings.TrimSpace(text); text != "" { 101 return text 102 } 103 } 104 } 105 106 // readDefaultString reads a single line from stdin, trimming if from spaces. If 107 // an empty line is entered, the default value is returned. 108 func (w *wizard) readDefaultString(def string) string { 109 for { 110 fmt.Printf("> ") 111 text, err := w.in.ReadString('\n') 112 if err != nil { 113 log.Crit("Failed to read user input", "err", err) 114 } 115 if text = strings.TrimSpace(text); text != "" { 116 return text 117 } 118 return def 119 } 120 } 121 122 // readInt reads a single line from stdin, trimming if from spaces, enforcing it 123 // to parse into an integer. 124 func (w *wizard) readInt() int { 125 for { 126 fmt.Printf("> ") 127 text, err := w.in.ReadString('\n') 128 if err != nil { 129 log.Crit("Failed to read user input", "err", err) 130 } 131 if text = strings.TrimSpace(text); text == "" { 132 continue 133 } 134 val, err := strconv.Atoi(strings.TrimSpace(text)) 135 if err != nil { 136 log.Error("Invalid input, expected integer", "err", err) 137 continue 138 } 139 return val 140 } 141 } 142 143 // readDefaultInt reads a single line from stdin, trimming if from spaces, enforcing 144 // it to parse into an integer. If an empty line is entered, the default value is 145 // returned. 146 func (w *wizard) readDefaultInt(def int) int { 147 for { 148 fmt.Printf("> ") 149 text, err := w.in.ReadString('\n') 150 if err != nil { 151 log.Crit("Failed to read user input", "err", err) 152 } 153 if text = strings.TrimSpace(text); text == "" { 154 return def 155 } 156 val, err := strconv.Atoi(strings.TrimSpace(text)) 157 if err != nil { 158 log.Error("Invalid input, expected integer", "err", err) 159 continue 160 } 161 return val 162 } 163 } 164 165 // readFloat reads a single line from stdin, trimming if from spaces, enforcing it 166 // to parse into a float. 167 func (w *wizard) readFloat() float64 { 168 for { 169 fmt.Printf("> ") 170 text, err := w.in.ReadString('\n') 171 if err != nil { 172 log.Crit("Failed to read user input", "err", err) 173 } 174 if text = strings.TrimSpace(text); text == "" { 175 continue 176 } 177 val, err := strconv.ParseFloat(strings.TrimSpace(text), 64) 178 if err != nil { 179 log.Error("Invalid input, expected float", "err", err) 180 continue 181 } 182 return val 183 } 184 } 185 186 // readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing 187 // it to parse into a float. If an empty line is entered, the default value is returned. 188 func (w *wizard) readDefaultFloat(def float64) float64 { 189 for { 190 fmt.Printf("> ") 191 text, err := w.in.ReadString('\n') 192 if err != nil { 193 log.Crit("Failed to read user input", "err", err) 194 } 195 if text = strings.TrimSpace(text); text == "" { 196 return def 197 } 198 val, err := strconv.ParseFloat(strings.TrimSpace(text), 64) 199 if err != nil { 200 log.Error("Invalid input, expected float", "err", err) 201 continue 202 } 203 return val 204 } 205 } 206 207 // readPassword reads a single line from stdin, trimming it from the trailing new 208 // line and returns it. The input will not be echoed. 209 func (w *wizard) readPassword() string { 210 for { 211 fmt.Printf("> ") 212 text, err := terminal.ReadPassword(int(syscall.Stdin)) 213 if err != nil { 214 log.Crit("Failed to read password", "err", err) 215 } 216 fmt.Println() 217 return string(text) 218 } 219 } 220 221 // readAddress reads a single line from stdin, trimming if from spaces and converts 222 // it to an Ethereum address. 223 func (w *wizard) readAddress() *common.Address { 224 for { 225 // Read the address from the user 226 fmt.Printf("> 0x") 227 text, err := w.in.ReadString('\n') 228 if err != nil { 229 log.Crit("Failed to read user input", "err", err) 230 } 231 if text = strings.TrimSpace(text); text == "" { 232 return nil 233 } 234 // Make sure it looks ok and return it if so 235 if len(text) != 40 { 236 log.Error("Invalid address length, please retry") 237 continue 238 } 239 bigaddr, _ := new(big.Int).SetString(text, 16) 240 address := common.BigToAddress(bigaddr) 241 return &address 242 } 243 } 244 245 // readDefaultAddress reads a single line from stdin, trimming if from spaces and 246 // converts it to an Ethereum address. If an empty line is entered, the default 247 // value is returned. 248 func (w *wizard) readDefaultAddress(def common.Address) common.Address { 249 for { 250 // Read the address from the user 251 fmt.Printf("> 0x") 252 text, err := w.in.ReadString('\n') 253 if err != nil { 254 log.Crit("Failed to read user input", "err", err) 255 } 256 if text = strings.TrimSpace(text); text == "" { 257 return def 258 } 259 // Make sure it looks ok and return it if so 260 if len(text) != 40 { 261 log.Error("Invalid address length, please retry") 262 continue 263 } 264 bigaddr, _ := new(big.Int).SetString(text, 16) 265 return common.BigToAddress(bigaddr) 266 } 267 } 268 269 // readJSON reads a raw JSON message and returns it. 270 func (w *wizard) readJSON() string { 271 var blob json.RawMessage 272 273 for { 274 fmt.Printf("> ") 275 if err := json.NewDecoder(w.in).Decode(&blob); err != nil { 276 log.Error("Invalid JSON, please try again", "err", err) 277 continue 278 } 279 return string(blob) 280 } 281 }