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  }