wizard.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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. package main
  17. import (
  18. "bufio"
  19. "encoding/json"
  20. "fmt"
  21. "io/ioutil"
  22. "math/big"
  23. "net"
  24. "os"
  25. "path/filepath"
  26. "sort"
  27. "strconv"
  28. "strings"
  29. "syscall"
  30. "github.com/ethereum/go-ethereum/common"
  31. "github.com/ethereum/go-ethereum/core"
  32. "github.com/ethereum/go-ethereum/log"
  33. "golang.org/x/crypto/ssh/terminal"
  34. )
  35. // config contains all the configurations needed by puppeth that should be saved
  36. // between sessions.
  37. type config struct {
  38. path string // File containing the configuration values
  39. genesis *core.Genesis // Genesis block to cache for node deploys
  40. bootFull []string // Bootnodes to always connect to by full nodes
  41. bootLight []string // Bootnodes to always connect to by light nodes
  42. ethstats string // Ethstats settings to cache for node deploys
  43. Servers map[string][]byte `json:"servers,omitempty"`
  44. }
  45. // servers retrieves an alphabetically sorted list of servers.
  46. func (c config) servers() []string {
  47. servers := make([]string, 0, len(c.Servers))
  48. for server := range c.Servers {
  49. servers = append(servers, server)
  50. }
  51. sort.Strings(servers)
  52. return servers
  53. }
  54. // flush dumps the contents of config to disk.
  55. func (c config) flush() {
  56. os.MkdirAll(filepath.Dir(c.path), 0755)
  57. out, _ := json.MarshalIndent(c, "", " ")
  58. if err := ioutil.WriteFile(c.path, out, 0644); err != nil {
  59. log.Warn("Failed to save puppeth configs", "file", c.path, "err", err)
  60. }
  61. }
  62. type wizard struct {
  63. network string // Network name to manage
  64. conf config // Configurations from previous runs
  65. servers map[string]*sshClient // SSH connections to servers to administer
  66. services map[string][]string // Ethereum services known to be running on servers
  67. in *bufio.Reader // Wrapper around stdin to allow reading user input
  68. }
  69. // read reads a single line from stdin, trimming if from spaces.
  70. func (w *wizard) read() string {
  71. fmt.Printf("> ")
  72. text, err := w.in.ReadString('\n')
  73. if err != nil {
  74. log.Crit("Failed to read user input", "err", err)
  75. }
  76. return strings.TrimSpace(text)
  77. }
  78. // readString reads a single line from stdin, trimming if from spaces, enforcing
  79. // non-emptyness.
  80. func (w *wizard) readString() string {
  81. for {
  82. fmt.Printf("> ")
  83. text, err := w.in.ReadString('\n')
  84. if err != nil {
  85. log.Crit("Failed to read user input", "err", err)
  86. }
  87. if text = strings.TrimSpace(text); text != "" {
  88. return text
  89. }
  90. }
  91. }
  92. // readDefaultString reads a single line from stdin, trimming if from spaces. If
  93. // an empty line is entered, the default value is returned.
  94. func (w *wizard) readDefaultString(def string) string {
  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. return def
  104. }
  105. // readInt reads a single line from stdin, trimming if from spaces, enforcing it
  106. // to parse into an integer.
  107. func (w *wizard) readInt() int {
  108. for {
  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. continue
  116. }
  117. val, err := strconv.Atoi(strings.TrimSpace(text))
  118. if err != nil {
  119. log.Error("Invalid input, expected integer", "err", err)
  120. continue
  121. }
  122. return val
  123. }
  124. }
  125. // readDefaultInt reads a single line from stdin, trimming if from spaces, enforcing
  126. // it to parse into an integer. If an empty line is entered, the default value is
  127. // returned.
  128. func (w *wizard) readDefaultInt(def int) int {
  129. for {
  130. fmt.Printf("> ")
  131. text, err := w.in.ReadString('\n')
  132. if err != nil {
  133. log.Crit("Failed to read user input", "err", err)
  134. }
  135. if text = strings.TrimSpace(text); text == "" {
  136. return def
  137. }
  138. val, err := strconv.Atoi(strings.TrimSpace(text))
  139. if err != nil {
  140. log.Error("Invalid input, expected integer", "err", err)
  141. continue
  142. }
  143. return val
  144. }
  145. }
  146. /*
  147. // readFloat reads a single line from stdin, trimming if from spaces, enforcing it
  148. // to parse into a float.
  149. func (w *wizard) readFloat() float64 {
  150. for {
  151. fmt.Printf("> ")
  152. text, err := w.in.ReadString('\n')
  153. if err != nil {
  154. log.Crit("Failed to read user input", "err", err)
  155. }
  156. if text = strings.TrimSpace(text); text == "" {
  157. continue
  158. }
  159. val, err := strconv.ParseFloat(strings.TrimSpace(text), 64)
  160. if err != nil {
  161. log.Error("Invalid input, expected float", "err", err)
  162. continue
  163. }
  164. return val
  165. }
  166. }
  167. */
  168. // readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing
  169. // it to parse into a float. If an empty line is entered, the default value is returned.
  170. func (w *wizard) readDefaultFloat(def float64) float64 {
  171. for {
  172. fmt.Printf("> ")
  173. text, err := w.in.ReadString('\n')
  174. if err != nil {
  175. log.Crit("Failed to read user input", "err", err)
  176. }
  177. if text = strings.TrimSpace(text); text == "" {
  178. return def
  179. }
  180. val, err := strconv.ParseFloat(strings.TrimSpace(text), 64)
  181. if err != nil {
  182. log.Error("Invalid input, expected float", "err", err)
  183. continue
  184. }
  185. return val
  186. }
  187. }
  188. // readPassword reads a single line from stdin, trimming it from the trailing new
  189. // line and returns it. The input will not be echoed.
  190. func (w *wizard) readPassword() string {
  191. fmt.Printf("> ")
  192. text, err := terminal.ReadPassword(int(syscall.Stdin))
  193. if err != nil {
  194. log.Crit("Failed to read password", "err", err)
  195. }
  196. fmt.Println()
  197. return string(text)
  198. }
  199. // readAddress reads a single line from stdin, trimming if from spaces and converts
  200. // it to an Ethereum address.
  201. func (w *wizard) readAddress() *common.Address {
  202. for {
  203. // Read the address from the user
  204. fmt.Printf("> 0x")
  205. text, err := w.in.ReadString('\n')
  206. if err != nil {
  207. log.Crit("Failed to read user input", "err", err)
  208. }
  209. if text = strings.TrimSpace(text); text == "" {
  210. return nil
  211. }
  212. // Make sure it looks ok and return it if so
  213. if len(text) != 40 {
  214. log.Error("Invalid address length, please retry")
  215. continue
  216. }
  217. bigaddr, _ := new(big.Int).SetString(text, 16)
  218. address := common.BigToAddress(bigaddr)
  219. return &address
  220. }
  221. }
  222. // readDefaultAddress reads a single line from stdin, trimming if from spaces and
  223. // converts it to an Ethereum address. If an empty line is entered, the default
  224. // value is returned.
  225. func (w *wizard) readDefaultAddress(def common.Address) common.Address {
  226. for {
  227. // Read the address from the user
  228. fmt.Printf("> 0x")
  229. text, err := w.in.ReadString('\n')
  230. if err != nil {
  231. log.Crit("Failed to read user input", "err", err)
  232. }
  233. if text = strings.TrimSpace(text); text == "" {
  234. return def
  235. }
  236. // Make sure it looks ok and return it if so
  237. if len(text) != 40 {
  238. log.Error("Invalid address length, please retry")
  239. continue
  240. }
  241. bigaddr, _ := new(big.Int).SetString(text, 16)
  242. return common.BigToAddress(bigaddr)
  243. }
  244. }
  245. // readJSON reads a raw JSON message and returns it.
  246. func (w *wizard) readJSON() string {
  247. var blob json.RawMessage
  248. for {
  249. fmt.Printf("> ")
  250. if err := json.NewDecoder(w.in).Decode(&blob); err != nil {
  251. log.Error("Invalid JSON, please try again", "err", err)
  252. continue
  253. }
  254. return string(blob)
  255. }
  256. }
  257. // readIPAddress reads a single line from stdin, trimming if from spaces and
  258. // converts it to a network IP address.
  259. func (w *wizard) readIPAddress() net.IP {
  260. for {
  261. // Read the IP address from the user
  262. fmt.Printf("> ")
  263. text, err := w.in.ReadString('\n')
  264. if err != nil {
  265. log.Crit("Failed to read user input", "err", err)
  266. }
  267. if text = strings.TrimSpace(text); text == "" {
  268. return nil
  269. }
  270. // Make sure it looks ok and return it if so
  271. ip := net.ParseIP(text)
  272. if ip == nil {
  273. log.Error("Invalid IP address, please retry")
  274. continue
  275. }
  276. return ip
  277. }
  278. }