github.com/reapchain/go-reapchain@v0.2.15-0.20210609012950-9735c110c705/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  }