github.com/cheng762/platon-go@v1.8.17-0.20190529111256-7deff2d7be26/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 "github.com/PlatONnetwork/PlatON-Go/p2p/discover" 21 "bufio" 22 "encoding/json" 23 "fmt" 24 "io/ioutil" 25 "math/big" 26 "net" 27 "os" 28 "path/filepath" 29 "sort" 30 "strconv" 31 "strings" 32 "sync" 33 34 "github.com/PlatONnetwork/PlatON-Go/common" 35 "github.com/PlatONnetwork/PlatON-Go/core" 36 "github.com/PlatONnetwork/PlatON-Go/log" 37 "golang.org/x/crypto/ssh/terminal" 38 ) 39 40 // config contains all the configurations needed by puppeth that should be saved 41 // between sessions. 42 type config struct { 43 path string // File containing the configuration values 44 bootnodes []string // Bootnodes to always connect to by all nodes 45 ethstats string // Ethstats settings to cache for node deploys 46 47 Genesis *core.Genesis `json:"genesis,omitempty"` // Genesis block to cache for node deploys 48 Servers map[string][]byte `json:"servers,omitempty"` 49 } 50 51 // servers retrieves an alphabetically sorted list of servers. 52 func (c config) servers() []string { 53 servers := make([]string, 0, len(c.Servers)) 54 for server := range c.Servers { 55 servers = append(servers, server) 56 } 57 sort.Strings(servers) 58 59 return servers 60 } 61 62 // flush dumps the contents of config to disk. 63 func (c config) flush() { 64 os.MkdirAll(filepath.Dir(c.path), 0755) 65 66 out, _ := json.MarshalIndent(c, "", " ") 67 if err := ioutil.WriteFile(c.path, out, 0644); err != nil { 68 log.Warn("Failed to save puppeth configs", "file", c.path, "err", err) 69 } 70 } 71 72 type wizard struct { 73 network string // Network name to manage 74 conf config // Configurations from previous runs 75 76 servers map[string]*sshClient // SSH connections to servers to administer 77 services map[string][]string // Ethereum services known to be running on servers 78 79 in *bufio.Reader // Wrapper around stdin to allow reading user input 80 lock sync.Mutex // Lock to protect configs during concurrent service discovery 81 } 82 83 // read reads a single line from stdin, trimming if from spaces. 84 func (w *wizard) read() string { 85 fmt.Printf("> ") 86 text, err := w.in.ReadString('\n') 87 if err != nil { 88 log.Crit("Failed to read user input", "err", err) 89 } 90 return strings.TrimSpace(text) 91 } 92 93 // readString reads a single line from stdin, trimming if from spaces, enforcing 94 // non-emptyness. 95 func (w *wizard) readString() string { 96 for { 97 fmt.Printf("> ") 98 text, err := w.in.ReadString('\n') 99 if err != nil { 100 log.Crit("Failed to read user input", "err", err) 101 } 102 if text = strings.TrimSpace(text); text != "" { 103 return text 104 } 105 } 106 } 107 108 // readDefaultString reads a single line from stdin, trimming if from spaces. If 109 // an empty line is entered, the default value is returned. 110 func (w *wizard) readDefaultString(def string) string { 111 fmt.Printf("> ") 112 text, err := w.in.ReadString('\n') 113 if err != nil { 114 log.Crit("Failed to read user input", "err", err) 115 } 116 if text = strings.TrimSpace(text); text != "" { 117 return text 118 } 119 return def 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 // readDefaultBigInt reads a single line from stdin, trimming if from spaces, 166 // enforcing it to parse into a big integer. If an empty line is entered, the 167 // default value is returned. 168 func (w *wizard) readDefaultBigInt(def *big.Int) *big.Int { 169 for { 170 fmt.Printf("> ") 171 text, err := w.in.ReadString('\n') 172 if err != nil { 173 log.Crit("Failed to read user input", "err", err) 174 } 175 if text = strings.TrimSpace(text); text == "" { 176 return def 177 } 178 val, ok := new(big.Int).SetString(text, 0) 179 if !ok { 180 log.Error("Invalid input, expected big integer") 181 continue 182 } 183 return val 184 } 185 } 186 187 /* 188 // readFloat reads a single line from stdin, trimming if from spaces, enforcing it 189 // to parse into a float. 190 func (w *wizard) readFloat() float64 { 191 for { 192 fmt.Printf("> ") 193 text, err := w.in.ReadString('\n') 194 if err != nil { 195 log.Crit("Failed to read user input", "err", err) 196 } 197 if text = strings.TrimSpace(text); text == "" { 198 continue 199 } 200 val, err := strconv.ParseFloat(strings.TrimSpace(text), 64) 201 if err != nil { 202 log.Error("Invalid input, expected float", "err", err) 203 continue 204 } 205 return val 206 } 207 } 208 */ 209 210 // readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing 211 // it to parse into a float. If an empty line is entered, the default value is returned. 212 func (w *wizard) readDefaultFloat(def float64) float64 { 213 for { 214 fmt.Printf("> ") 215 text, err := w.in.ReadString('\n') 216 if err != nil { 217 log.Crit("Failed to read user input", "err", err) 218 } 219 if text = strings.TrimSpace(text); text == "" { 220 return def 221 } 222 val, err := strconv.ParseFloat(strings.TrimSpace(text), 64) 223 if err != nil { 224 log.Error("Invalid input, expected float", "err", err) 225 continue 226 } 227 return val 228 } 229 } 230 231 // readPassword reads a single line from stdin, trimming it from the trailing new 232 // line and returns it. The input will not be echoed. 233 func (w *wizard) readPassword() string { 234 fmt.Printf("> ") 235 text, err := terminal.ReadPassword(int(os.Stdin.Fd())) 236 if err != nil { 237 log.Crit("Failed to read password", "err", err) 238 } 239 fmt.Println() 240 return string(text) 241 } 242 243 // readAddress reads a single line from stdin, trimming if from spaces and converts 244 // it to an Ethereum address. 245 func (w *wizard) readAddress() *common.Address { 246 for { 247 // Read the address from the user 248 fmt.Printf("> 0x") 249 text, err := w.in.ReadString('\n') 250 if err != nil { 251 log.Crit("Failed to read user input", "err", err) 252 } 253 if text = strings.TrimSpace(text); text == "" { 254 return nil 255 } 256 // Make sure it looks ok and return it if so 257 if len(text) != 40 { 258 log.Error("Invalid address length, please retry") 259 continue 260 } 261 bigaddr, _ := new(big.Int).SetString(text, 16) 262 address := common.BigToAddress(bigaddr) 263 return &address 264 } 265 } 266 267 // readDefaultAddress reads a single line from stdin, trimming if from spaces and 268 // converts it to an Ethereum address. If an empty line is entered, the default 269 // value is returned. 270 func (w *wizard) readDefaultAddress(def common.Address) common.Address { 271 for { 272 // Read the address from the user 273 fmt.Printf("> 0x") 274 text, err := w.in.ReadString('\n') 275 if err != nil { 276 log.Crit("Failed to read user input", "err", err) 277 } 278 if text = strings.TrimSpace(text); text == "" { 279 return def 280 } 281 // Make sure it looks ok and return it if so 282 if len(text) != 40 { 283 log.Error("Invalid address length, please retry") 284 continue 285 } 286 bigaddr, _ := new(big.Int).SetString(text, 16) 287 return common.BigToAddress(bigaddr) 288 } 289 } 290 291 // readNodeURL reads a single line from stdin, parse and convert 292 // it to an Ethereum Node. 293 func (w *wizard) readNodeURL() *discover.Node { 294 for { 295 // Read the url from the user 296 fmt.Printf("> enode://") 297 text, err := w.in.ReadString('\n') 298 if err != nil { 299 log.Crit("Failed to read user input", "err", err) 300 } 301 if text = strings.TrimSpace(text); text == "" { 302 return nil 303 } 304 305 // Make sure it looks ok and return it if so 306 if len(text) < 140 { 307 log.Error("Invalid url length, please retry") 308 continue 309 } 310 if !strings.Contains(text, "@")|| 311 !strings.Contains(text, ":")|| 312 3 != strings.Count(text, "."){ 313 log.Error("Invalid url format, please retry") 314 continue 315 } 316 text = "enode://" + text 317 node, err := discover.ParseNode(text) 318 if err != nil { 319 log.Error("Bootstrap URL invalid", "enode", text, "err", err) 320 } 321 322 return node 323 } 324 } 325 326 //func (w *wizard) readNodeID() discover.NodeID { 327 // for { 328 // // Read the url from the user 329 // fmt.Printf("> 0x") 330 // text, err := w.in.ReadString('\n') 331 // if err != nil { 332 // log.Crit("Failed to read user input", "err", err) 333 // } 334 // if text = strings.TrimSpace(text); text == "" { 335 // return discover.NodeID{0} 336 // } 337 // 338 // // Make sure it looks ok and return it if so 339 // if len(text) < 128 { 340 // log.Error("Invalid url length, please retry") 341 // continue 342 // } 343 // node,err := discover.BytesID(common.Hex2Bytes(text)) 344 // if err != nil { 345 // log.Error("Node ID invalid", "NodeID", text, "err", err) 346 // } 347 // 348 // return node 349 // } 350 //} 351 352 // readPrivate reads a single line from stdin, parse and convert 353 // it to an Ethereum ecdsa private key. 354 /*func (w *wizard) readPrivateKey() *ecdsa.PrivateKey { 355 for { 356 // Read the url from the user 357 fmt.Printf("> 0x") 358 text, err := w.in.ReadString('\n') 359 if err != nil { 360 log.Crit("Failed to read user input", "err", err) 361 } 362 if text = strings.TrimSpace(text); text == "" { 363 return nil 364 } 365 366 // Make sure it looks ok and return it if so 367 if len(text) != 64 { 368 log.Error("Invalid private key length, please retry") 369 continue 370 } 371 prikey, err := crypto.ToECDSA(common.Hex2Bytes(text)) 372 if err != nil { 373 log.Error("Invalid private key", "private key", text, "err", err) 374 } 375 376 return prikey 377 } 378 }*/ 379 380 // readJSON reads a raw JSON message and returns it. 381 func (w *wizard) readJSON() string { 382 var blob json.RawMessage 383 384 for { 385 fmt.Printf("> ") 386 if err := json.NewDecoder(w.in).Decode(&blob); err != nil { 387 log.Error("Invalid JSON, please try again", "err", err) 388 continue 389 } 390 return string(blob) 391 } 392 } 393 394 // readIPAddress reads a single line from stdin, trimming if from spaces and 395 // returning it if it's convertible to an IP address. The reason for keeping 396 // the user input format instead of returning a Go net.IP is to match with 397 // weird formats used by ethstats, which compares IPs textually, not by value. 398 func (w *wizard) readIPAddress() string { 399 for { 400 // Read the IP address from the user 401 fmt.Printf("> ") 402 text, err := w.in.ReadString('\n') 403 if err != nil { 404 log.Crit("Failed to read user input", "err", err) 405 } 406 if text = strings.TrimSpace(text); text == "" { 407 return "" 408 } 409 // Make sure it looks ok and return it if so 410 if ip := net.ParseIP(text); ip == nil { 411 log.Error("Invalid IP address, please retry") 412 continue 413 } 414 return text 415 } 416 }