github.com/ConsenSys/Quorum@v20.10.0+incompatible/cmd/geth/consolecmd.go (about) 1 // Copyright 2016 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 "context" 21 "crypto/tls" 22 "crypto/x509" 23 "fmt" 24 "io/ioutil" 25 "net/http" 26 "net/url" 27 "os" 28 "os/signal" 29 "path/filepath" 30 "strings" 31 "syscall" 32 33 "github.com/ethereum/go-ethereum/cmd/utils" 34 "github.com/ethereum/go-ethereum/console" 35 "github.com/ethereum/go-ethereum/log" 36 "github.com/ethereum/go-ethereum/node" 37 "github.com/ethereum/go-ethereum/plugin/security" 38 "github.com/ethereum/go-ethereum/rpc" 39 "gopkg.in/urfave/cli.v1" 40 ) 41 42 var ( 43 consoleFlags = []cli.Flag{utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag} 44 rpcClientFlags = []cli.Flag{utils.RPCClientToken, utils.RPCClientTLSCert, utils.RPCClientTLSCaCert, utils.RPCClientTLSCipherSuites, utils.RPCClientTLSInsecureSkipVerify} 45 46 consoleCommand = cli.Command{ 47 Action: utils.MigrateFlags(localConsole), 48 Name: "console", 49 Usage: "Start an interactive JavaScript environment", 50 Flags: append(append(append(nodeFlags, rpcFlags...), consoleFlags...), whisperFlags...), 51 Category: "CONSOLE COMMANDS", 52 Description: ` 53 The Geth console is an interactive shell for the JavaScript runtime environment 54 which exposes a node admin interface as well as the Ðapp JavaScript API. 55 See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.`, 56 } 57 58 attachCommand = cli.Command{ 59 Action: utils.MigrateFlags(remoteConsole), 60 Name: "attach", 61 Usage: "Start an interactive JavaScript environment (connect to node)", 62 ArgsUsage: "[endpoint]", 63 Flags: append(append(consoleFlags, utils.DataDirFlag), rpcClientFlags...), 64 Category: "CONSOLE COMMANDS", 65 Description: ` 66 The Geth console is an interactive shell for the JavaScript runtime environment 67 which exposes a node admin interface as well as the Ðapp JavaScript API. 68 See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console. 69 This command allows to open a console on a running geth node.`, 70 } 71 72 javascriptCommand = cli.Command{ 73 Action: utils.MigrateFlags(ephemeralConsole), 74 Name: "js", 75 Usage: "Execute the specified JavaScript files", 76 ArgsUsage: "<jsfile> [jsfile...]", 77 Flags: append(nodeFlags, consoleFlags...), 78 Category: "CONSOLE COMMANDS", 79 Description: ` 80 The JavaScript VM exposes a node admin interface as well as the Ðapp 81 JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console`, 82 } 83 ) 84 85 // Quorum 86 // 87 // read tls client configuration from command line arguments 88 // 89 // only for HTTPS or WSS 90 func readTLSClientConfig(endpoint string, ctx *cli.Context) (*tls.Config, bool, error) { 91 if !strings.HasPrefix(endpoint, "https://") && !strings.HasPrefix(endpoint, "wss://") { 92 return nil, false, nil 93 } 94 hasCustomTls := false 95 insecureSkipVerify := ctx.Bool(utils.RPCClientTLSInsecureSkipVerify.Name) 96 tlsConfig := &tls.Config{ 97 InsecureSkipVerify: insecureSkipVerify, 98 } 99 var certFile, caFile string 100 if !insecureSkipVerify { 101 var certPem, caPem []byte 102 certFile, caFile = ctx.String(utils.RPCClientTLSCert.Name), ctx.String(utils.RPCClientTLSCaCert.Name) 103 var err error 104 if certFile != "" { 105 if certPem, err = ioutil.ReadFile(certFile); err != nil { 106 return nil, true, err 107 } 108 } 109 if caFile != "" { 110 if caPem, err = ioutil.ReadFile(caFile); err != nil { 111 return nil, true, err 112 } 113 } 114 if len(certPem) != 0 || len(caPem) != 0 { 115 certPool, err := x509.SystemCertPool() 116 if err != nil { 117 certPool = x509.NewCertPool() 118 } 119 if len(certPem) != 0 { 120 certPool.AppendCertsFromPEM(certPem) 121 } 122 if len(caPem) != 0 { 123 certPool.AppendCertsFromPEM(caPem) 124 } 125 tlsConfig.RootCAs = certPool 126 hasCustomTls = true 127 } 128 } else { 129 hasCustomTls = true 130 } 131 cipherSuitesInput := ctx.String(utils.RPCClientTLSCipherSuites.Name) 132 cipherSuitesStrings := strings.FieldsFunc(cipherSuitesInput, func(r rune) bool { 133 return r == ',' 134 }) 135 if len(cipherSuitesStrings) > 0 { 136 cipherSuiteList := make(security.CipherSuiteList, len(cipherSuitesStrings)) 137 for i, s := range cipherSuitesStrings { 138 cipherSuiteList[i] = security.CipherSuite(strings.TrimSpace(s)) 139 } 140 cipherSuites, err := cipherSuiteList.ToUint16Array() 141 if err != nil { 142 return nil, true, err 143 } 144 tlsConfig.CipherSuites = cipherSuites 145 hasCustomTls = true 146 } 147 if !hasCustomTls { 148 return nil, false, nil 149 } 150 return tlsConfig, hasCustomTls, nil 151 } 152 153 // localConsole starts a new geth node, attaching a JavaScript console to it at the 154 // same time. 155 func localConsole(ctx *cli.Context) error { 156 // Create and start the node based on the CLI flags 157 prepare(ctx) 158 node := makeFullNode(ctx) 159 startNode(ctx, node) 160 defer node.Close() 161 162 // Attach to the newly started node and start the JavaScript console 163 client, err := node.Attach() 164 if err != nil { 165 utils.Fatalf("Failed to attach to the inproc geth: %v", err) 166 } 167 config := console.Config{ 168 DataDir: utils.MakeDataDir(ctx), 169 DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), 170 Client: client, 171 Preload: utils.MakeConsolePreloads(ctx), 172 } 173 174 console, err := console.New(config) 175 if err != nil { 176 utils.Fatalf("Failed to start the JavaScript console: %v", err) 177 } 178 defer console.Stop(false) 179 180 // If only a short execution was requested, evaluate and return 181 if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { 182 console.Evaluate(script) 183 return nil 184 } 185 // Otherwise print the welcome screen and enter interactive mode 186 console.Welcome() 187 console.Interactive() 188 189 return nil 190 } 191 192 // remoteConsole will connect to a remote geth instance, attaching a JavaScript 193 // console to it. 194 func remoteConsole(ctx *cli.Context) error { 195 // Attach to a remotely running geth instance and start the JavaScript console 196 endpoint := ctx.Args().First() 197 if endpoint == "" { 198 path := node.DefaultDataDir() 199 if ctx.GlobalIsSet(utils.DataDirFlag.Name) { 200 path = ctx.GlobalString(utils.DataDirFlag.Name) 201 } 202 if path != "" { 203 if ctx.GlobalBool(utils.TestnetFlag.Name) { 204 path = filepath.Join(path, "testnet") 205 } else if ctx.GlobalBool(utils.RinkebyFlag.Name) { 206 path = filepath.Join(path, "rinkeby") 207 } 208 } 209 endpoint = fmt.Sprintf("%s/geth.ipc", path) 210 } 211 client, err := dialRPC(endpoint, ctx) 212 if err != nil { 213 utils.Fatalf("Unable to attach to remote geth: %v", err) 214 } 215 config := console.Config{ 216 DataDir: utils.MakeDataDir(ctx), 217 DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), 218 Client: client, 219 Preload: utils.MakeConsolePreloads(ctx), 220 } 221 222 consl, err := console.New(config) 223 if err != nil { 224 utils.Fatalf("Failed to start the JavaScript console: %v", err) 225 } 226 defer consl.Stop(false) 227 228 if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { 229 consl.Evaluate(script) 230 return nil 231 } 232 233 // Otherwise print the welcome screen and enter interactive mode 234 consl.Welcome() 235 consl.Interactive() 236 237 return nil 238 } 239 240 // dialRPC returns a RPC client which connects to the given endpoint. 241 // The check for empty endpoint implements the defaulting logic 242 // for "geth attach" and "geth monitor" with no argument. 243 // 244 // Quorum: passing the cli context to build security-aware client: 245 // 1. Custom TLS configuration 246 // 2. Access Token awareness via rpc.HttpCredentialsProviderFunc 247 func dialRPC(endpoint string, ctx *cli.Context) (*rpc.Client, error) { 248 if endpoint == "" { 249 endpoint = node.DefaultIPCEndpoint(clientIdentifier) 250 } else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") { 251 // Backwards compatibility with geth < 1.5 which required 252 // these prefixes. 253 endpoint = endpoint[4:] 254 } 255 var ( 256 client *rpc.Client 257 err error 258 dialCtx = context.Background() 259 ) 260 tlsConfig, hasCustomTls, tlsReadErr := readTLSClientConfig(endpoint, ctx) 261 if tlsReadErr != nil { 262 return nil, tlsReadErr 263 } 264 if token := ctx.String(utils.RPCClientToken.Name); token != "" { 265 var f rpc.HttpCredentialsProviderFunc = func(ctx context.Context) (string, error) { 266 return token, nil 267 } 268 // it's important that f MUST BE OF TYPE rpc.HttpCredentialsProviderFunc 269 dialCtx = context.WithValue(dialCtx, rpc.CtxCredentialsProvider, f) 270 } 271 if hasCustomTls { 272 u, err := url.Parse(endpoint) 273 if err != nil { 274 return nil, err 275 } 276 switch u.Scheme { 277 case "https": 278 customHttpClient := &http.Client{ 279 Transport: http.DefaultTransport, 280 } 281 customHttpClient.Transport.(*http.Transport).TLSClientConfig = tlsConfig 282 client, err = rpc.DialHTTPWithClient(endpoint, customHttpClient) 283 case "wss": 284 client, err = rpc.DialWebsocketWithCustomTLS(dialCtx, endpoint, "", tlsConfig) 285 default: 286 log.Warn("unsupported scheme for custom TLS which is only for HTTPS/WSS", "scheme", u.Scheme) 287 client, err = rpc.DialContext(dialCtx, endpoint) 288 } 289 } else { 290 client, err = rpc.DialContext(dialCtx, endpoint) 291 } 292 if f, ok := dialCtx.Value(rpc.CtxCredentialsProvider).(rpc.HttpCredentialsProviderFunc); ok && err == nil { 293 client, err = client.WithHTTPCredentials(f) 294 } 295 return client, err 296 } 297 298 // ephemeralConsole starts a new geth node, attaches an ephemeral JavaScript 299 // console to it, executes each of the files specified as arguments and tears 300 // everything down. 301 func ephemeralConsole(ctx *cli.Context) error { 302 // Create and start the node based on the CLI flags 303 node := makeFullNode(ctx) 304 startNode(ctx, node) 305 defer node.Close() 306 307 // Attach to the newly started node and start the JavaScript console 308 client, err := node.Attach() 309 if err != nil { 310 utils.Fatalf("Failed to attach to the inproc geth: %v", err) 311 } 312 config := console.Config{ 313 DataDir: utils.MakeDataDir(ctx), 314 DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), 315 Client: client, 316 Preload: utils.MakeConsolePreloads(ctx), 317 } 318 319 console, err := console.New(config) 320 if err != nil { 321 utils.Fatalf("Failed to start the JavaScript console: %v", err) 322 } 323 defer console.Stop(false) 324 325 // Evaluate each of the specified JavaScript files 326 for _, file := range ctx.Args() { 327 if err = console.Execute(file); err != nil { 328 utils.Fatalf("Failed to execute %s: %v", file, err) 329 } 330 } 331 // Wait for pending callbacks, but stop for Ctrl-C. 332 abort := make(chan os.Signal, 1) 333 signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM) 334 335 go func() { 336 <-abort 337 os.Exit(0) 338 }() 339 console.Stop(true) 340 341 return nil 342 }