github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/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 "sync" 32 33 "github.com/vntchain/go-vnt/common" 34 "github.com/vntchain/go-vnt/core" 35 "github.com/vntchain/go-vnt/log" 36 "github.com/vntchain/go-vnt/vntp2p" 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 vntstats string // Vntstats 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 // VNT 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 VNT 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 func (w *wizard) readNodeInfo() string { 268 for { 269 fmt.Printf(">") 270 if text, err := w.in.ReadString('\n'); err != nil { 271 log.Error("Failed to read user input, please retry") 272 continue 273 } else { 274 text = strings.Replace(text, "\n", "", 1) 275 if text == "" { 276 return "" 277 } 278 if _, err = vntp2p.ParseNode(text); err != nil { 279 log.Error("ParseNode err, please retry", "err", err) 280 continue 281 } else { 282 return text 283 } 284 } 285 } 286 } 287 288 // readDefaultAddress reads a single line from stdin, trimming if from spaces and 289 // converts it to an VNT address. If an empty line is entered, the default 290 // value is returned. 291 func (w *wizard) readDefaultAddress(def common.Address) common.Address { 292 for { 293 // Read the address from the user 294 fmt.Printf("> 0x") 295 text, err := w.in.ReadString('\n') 296 if err != nil { 297 log.Crit("Failed to read user input", "err", err) 298 } 299 if text = strings.TrimSpace(text); text == "" { 300 return def 301 } 302 // Make sure it looks ok and return it if so 303 if len(text) != 40 { 304 log.Error("Invalid address length, please retry") 305 continue 306 } 307 bigaddr, _ := new(big.Int).SetString(text, 16) 308 return common.BigToAddress(bigaddr) 309 } 310 } 311 312 // readJSON reads a raw JSON message and returns it. 313 func (w *wizard) readJSON() string { 314 var blob json.RawMessage 315 316 for { 317 fmt.Printf("> ") 318 if err := json.NewDecoder(w.in).Decode(&blob); err != nil { 319 log.Error("Invalid JSON, please try again", "err", err) 320 continue 321 } 322 return string(blob) 323 } 324 } 325 326 // readIPAddress reads a single line from stdin, trimming if from spaces and 327 // returning it if it's convertible to an IP address. The reason for keeping 328 // the user input format instead of returning a Go net.IP is to match with 329 // weird formats used by vntstats, which compares IPs textually, not by value. 330 func (w *wizard) readIPAddress() string { 331 for { 332 // Read the IP address from the user 333 fmt.Printf("> ") 334 text, err := w.in.ReadString('\n') 335 if err != nil { 336 log.Crit("Failed to read user input", "err", err) 337 } 338 if text = strings.TrimSpace(text); text == "" { 339 return "" 340 } 341 // Make sure it looks ok and return it if so 342 if ip := net.ParseIP(text); ip == nil { 343 log.Error("Invalid IP address, please retry") 344 continue 345 } 346 return text 347 } 348 }