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  }