github.com/tuotoo/go-ethereum@v1.7.4-0.20171121184211-049797d40a24/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 "net" 26 "os" 27 "path/filepath" 28 "sort" 29 "strconv" 30 "strings" 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 fmt.Printf("> ") 110 text, err := w.in.ReadString('\n') 111 if err != nil { 112 log.Crit("Failed to read user input", "err", err) 113 } 114 if text = strings.TrimSpace(text); text != "" { 115 return text 116 } 117 return def 118 } 119 120 // readInt reads a single line from stdin, trimming if from spaces, enforcing it 121 // to parse into an integer. 122 func (w *wizard) readInt() int { 123 for { 124 fmt.Printf("> ") 125 text, err := w.in.ReadString('\n') 126 if err != nil { 127 log.Crit("Failed to read user input", "err", err) 128 } 129 if text = strings.TrimSpace(text); text == "" { 130 continue 131 } 132 val, err := strconv.Atoi(strings.TrimSpace(text)) 133 if err != nil { 134 log.Error("Invalid input, expected integer", "err", err) 135 continue 136 } 137 return val 138 } 139 } 140 141 // readDefaultInt reads a single line from stdin, trimming if from spaces, enforcing 142 // it to parse into an integer. If an empty line is entered, the default value is 143 // returned. 144 func (w *wizard) readDefaultInt(def int) int { 145 for { 146 fmt.Printf("> ") 147 text, err := w.in.ReadString('\n') 148 if err != nil { 149 log.Crit("Failed to read user input", "err", err) 150 } 151 if text = strings.TrimSpace(text); text == "" { 152 return def 153 } 154 val, err := strconv.Atoi(strings.TrimSpace(text)) 155 if err != nil { 156 log.Error("Invalid input, expected integer", "err", err) 157 continue 158 } 159 return val 160 } 161 } 162 163 // readDefaultBigInt reads a single line from stdin, trimming if from spaces, 164 // enforcing it to parse into a big integer. If an empty line is entered, the 165 // default value is returned. 166 func (w *wizard) readDefaultBigInt(def *big.Int) *big.Int { 167 for { 168 fmt.Printf("> ") 169 text, err := w.in.ReadString('\n') 170 if err != nil { 171 log.Crit("Failed to read user input", "err", err) 172 } 173 if text = strings.TrimSpace(text); text == "" { 174 return def 175 } 176 val, ok := new(big.Int).SetString(text, 0) 177 if !ok { 178 log.Error("Invalid input, expected big integer") 179 continue 180 } 181 return val 182 } 183 } 184 185 /* 186 // readFloat reads a single line from stdin, trimming if from spaces, enforcing it 187 // to parse into a float. 188 func (w *wizard) readFloat() 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 continue 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 208 // readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing 209 // it to parse into a float. If an empty line is entered, the default value is returned. 210 func (w *wizard) readDefaultFloat(def float64) float64 { 211 for { 212 fmt.Printf("> ") 213 text, err := w.in.ReadString('\n') 214 if err != nil { 215 log.Crit("Failed to read user input", "err", err) 216 } 217 if text = strings.TrimSpace(text); text == "" { 218 return def 219 } 220 val, err := strconv.ParseFloat(strings.TrimSpace(text), 64) 221 if err != nil { 222 log.Error("Invalid input, expected float", "err", err) 223 continue 224 } 225 return val 226 } 227 } 228 229 // readPassword reads a single line from stdin, trimming it from the trailing new 230 // line and returns it. The input will not be echoed. 231 func (w *wizard) readPassword() string { 232 fmt.Printf("> ") 233 text, err := terminal.ReadPassword(int(os.Stdin.Fd())) 234 if err != nil { 235 log.Crit("Failed to read password", "err", err) 236 } 237 fmt.Println() 238 return string(text) 239 } 240 241 // readAddress reads a single line from stdin, trimming if from spaces and converts 242 // it to an Ethereum address. 243 func (w *wizard) readAddress() *common.Address { 244 for { 245 // Read the address from the user 246 fmt.Printf("> 0x") 247 text, err := w.in.ReadString('\n') 248 if err != nil { 249 log.Crit("Failed to read user input", "err", err) 250 } 251 if text = strings.TrimSpace(text); text == "" { 252 return nil 253 } 254 // Make sure it looks ok and return it if so 255 if len(text) != 40 { 256 log.Error("Invalid address length, please retry") 257 continue 258 } 259 bigaddr, _ := new(big.Int).SetString(text, 16) 260 address := common.BigToAddress(bigaddr) 261 return &address 262 } 263 } 264 265 // readDefaultAddress reads a single line from stdin, trimming if from spaces and 266 // converts it to an Ethereum address. If an empty line is entered, the default 267 // value is returned. 268 func (w *wizard) readDefaultAddress(def common.Address) common.Address { 269 for { 270 // Read the address from the user 271 fmt.Printf("> 0x") 272 text, err := w.in.ReadString('\n') 273 if err != nil { 274 log.Crit("Failed to read user input", "err", err) 275 } 276 if text = strings.TrimSpace(text); text == "" { 277 return def 278 } 279 // Make sure it looks ok and return it if so 280 if len(text) != 40 { 281 log.Error("Invalid address length, please retry") 282 continue 283 } 284 bigaddr, _ := new(big.Int).SetString(text, 16) 285 return common.BigToAddress(bigaddr) 286 } 287 } 288 289 // readJSON reads a raw JSON message and returns it. 290 func (w *wizard) readJSON() string { 291 var blob json.RawMessage 292 293 for { 294 fmt.Printf("> ") 295 if err := json.NewDecoder(w.in).Decode(&blob); err != nil { 296 log.Error("Invalid JSON, please try again", "err", err) 297 continue 298 } 299 return string(blob) 300 } 301 } 302 303 // readIPAddress reads a single line from stdin, trimming if from spaces and 304 // returning it if it's convertible to an IP address. The reason for keeping 305 // the user input format instead of returning a Go net.IP is to match with 306 // weird formats used by ethstats, which compares IPs textually, not by value. 307 func (w *wizard) readIPAddress() string { 308 for { 309 // Read the IP address from the user 310 fmt.Printf("> ") 311 text, err := w.in.ReadString('\n') 312 if err != nil { 313 log.Crit("Failed to read user input", "err", err) 314 } 315 if text = strings.TrimSpace(text); text == "" { 316 return "" 317 } 318 // Make sure it looks ok and return it if so 319 if ip := net.ParseIP(text); ip == nil { 320 log.Error("Invalid IP address, please retry") 321 continue 322 } 323 return text 324 } 325 }