diff options
Diffstat (limited to 'cmd/geth')
-rw-r--r-- | cmd/geth/js.go | 172 | ||||
-rw-r--r-- | cmd/geth/js_test.go | 101 | ||||
-rw-r--r-- | cmd/geth/main.go | 51 | ||||
-rw-r--r-- | cmd/geth/monitorcmd.go | 47 | ||||
-rw-r--r-- | cmd/geth/usage.go | 8 |
5 files changed, 196 insertions, 183 deletions
diff --git a/cmd/geth/js.go b/cmd/geth/js.go index cdafab7fa..3d0251f08 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -24,23 +24,16 @@ import ( "os/signal" "path/filepath" "regexp" - "strings" - "sort" + "strings" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/natspec" "github.com/ethereum/go-ethereum/common/registrar" "github.com/ethereum/go-ethereum/eth" re "github.com/ethereum/go-ethereum/jsre" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/rpc/api" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" "github.com/peterh/liner" "github.com/robertkrimen/otto" ) @@ -79,82 +72,90 @@ func (r dumbterm) AppendHistory(string) {} type jsre struct { re *re.JSRE stack *node.Node - xeth *xeth.XEth wait chan *big.Int ps1 string atexit func() corsDomain string - client comms.EthereumClient + client rpc.Client prompter } var ( - loadedModulesMethods map[string][]string + loadedModulesMethods map[string][]string + autoCompleteStatement = "function _autocomplete(obj) {var results = []; for (var e in obj) { results.push(e); }; return results; }; _autocomplete(%s)" ) -func keywordCompleter(line string) []string { - results := make([]string, 0) - - if strings.Contains(line, ".") { - elements := strings.Split(line, ".") - if len(elements) == 2 { - module := elements[0] - partialMethod := elements[1] - if methods, found := loadedModulesMethods[module]; found { - for _, method := range methods { - if strings.HasPrefix(method, partialMethod) { // e.g. debug.se - results = append(results, module+"."+method) - } - } - } - } - } else { - for module, methods := range loadedModulesMethods { - if line == module { // user typed in full module name, show all methods - for _, method := range methods { - results = append(results, module+"."+method) +func keywordCompleter(jsre *jsre, line string) []string { + var results []string + parts := strings.Split(line, ".") + objRef := "this" + prefix := line + if len(parts) > 1 { + objRef = strings.Join(parts[0:len(parts) - 1], ".") + prefix = parts[len(parts) - 1] + } + + result, _ := jsre.re.Run(fmt.Sprintf(autoCompleteStatement, objRef)) + raw, _ := result.Export() + if keys, ok := raw.([]interface{}); ok { + for _, k := range keys { + if strings.HasPrefix(fmt.Sprintf("%s", k), prefix) { + if objRef == "this" { + results = append(results, fmt.Sprintf("%s", k)) + } else { + results = append(results, fmt.Sprintf("%s.%s", strings.Join(parts[:len(parts) - 1], "."), k)) } - } else if strings.HasPrefix(module, line) { // partial method name, e.g. admi - results = append(results, module) } } } - return results -} -func apiWordCompleter(line string, pos int) (head string, completions []string, tail string) { - if len(line) == 0 || pos == 0 { - return "", nil, "" + // e.g. web3<tab><tab> append dot since its an object + isObj, _ := jsre.re.Run(fmt.Sprintf("typeof(%s) === 'object'", line)) + if isObject, _ := isObj.ToBoolean(); isObject { + results = append(results, line + ".") } - i := 0 - for i = pos - 1; i > 0; i-- { - if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') { - continue + sort.Strings(results) + return results +} + +func apiWordCompleterWithContext(jsre *jsre) liner.WordCompleter { + completer := func(line string, pos int) (head string, completions []string, tail string) { + if len(line) == 0 || pos == 0 { + return "", nil, "" } - if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' { - continue + + // chuck data to relevant part for autocompletion, e.g. in case of nested lines eth.getBalance(eth.coinb<tab><tab> + i := 0 + for i = pos - 1; i > 0; i-- { + if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') { + continue + } + if i >= 3 && line[i] == '3' && line[i - 3] == 'w' && line[i - 2] == 'e' && line[i - 1] == 'b' { + continue + } + i += 1 + break } - i += 1 - break - } - begin := line[:i] - keyword := line[i:pos] - end := line[pos:] + begin := line[:i] + keyword := line[i:pos] + end := line[pos:] + + completionWords := keywordCompleter(jsre, keyword) + return begin, completionWords, end + } - completionWords := keywordCompleter(keyword) - return begin, completionWords, end + return completer } -func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir string, interactive bool) *jsre { +func newLightweightJSRE(docRoot string, client rpc.Client, datadir string, interactive bool) *jsre { js := &jsre{ps1: "> "} js.wait = make(chan *big.Int) js.client = client - // update state in separare forever blocks js.re = re.New(docRoot) - if err := js.apiBindings(js); err != nil { + if err := js.apiBindings(); err != nil { utils.Fatalf("Unable to initialize console - %v", err) } @@ -165,7 +166,7 @@ func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir str js.withHistory(datadir, func(hist *os.File) { lr.ReadHistory(hist) }) lr.SetCtrlCAborts(true) js.loadAutoCompletion() - lr.SetWordCompleter(apiWordCompleter) + lr.SetWordCompleter(apiWordCompleterWithContext(js)) lr.SetTabCompletionStyle(liner.TabPrints) js.prompter = lr js.atexit = func() { @@ -177,25 +178,15 @@ func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir str return js } -func newJSRE(stack *node.Node, docRoot, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre { +func newJSRE(stack *node.Node, docRoot, corsDomain string, client rpc.Client, interactive bool) *jsre { js := &jsre{stack: stack, ps1: "> "} // set default cors domain used by startRpc from CLI flag js.corsDomain = corsDomain - if f == nil { - f = js - } - js.xeth = xeth.New(stack, f) - js.wait = js.xeth.UpdateState() + js.wait = make(chan *big.Int) js.client = client - if clt, ok := js.client.(*comms.InProcClient); ok { - if offeredApis, err := api.ParseApiString(shared.AllApis, codec.JSON, js.xeth, stack); err == nil { - clt.Initialize(api.Merge(offeredApis...)) - } - } - // update state in separare forever blocks js.re = re.New(docRoot) - if err := js.apiBindings(f); err != nil { + if err := js.apiBindings(); err != nil { utils.Fatalf("Unable to connect - %v", err) } @@ -206,7 +197,7 @@ func newJSRE(stack *node.Node, docRoot, corsDomain string, client comms.Ethereum js.withHistory(stack.DataDir(), func(hist *os.File) { lr.ReadHistory(hist) }) lr.SetCtrlCAborts(true) js.loadAutoCompletion() - lr.SetWordCompleter(apiWordCompleter) + lr.SetWordCompleter(apiWordCompleterWithContext(js)) lr.SetTabCompletionStyle(liner.TabPrints) js.prompter = lr js.atexit = func() { @@ -222,7 +213,7 @@ func (self *jsre) loadAutoCompletion() { if modules, err := self.supportedApis(); err == nil { loadedModulesMethods = make(map[string][]string) for module, _ := range modules { - loadedModulesMethods[module] = api.AutoCompletion[module] + loadedModulesMethods[module] = rpc.AutoCompletion[module] } } } @@ -258,7 +249,6 @@ func (self *jsre) welcome() { loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version)) } sort.Strings(loadedModules) - } } @@ -266,7 +256,7 @@ func (self *jsre) supportedApis() (map[string]string, error) { return self.client.SupportedModules() } -func (js *jsre) apiBindings(f xeth.Frontend) error { +func (js *jsre) apiBindings() error { apis, err := js.supportedApis() if err != nil { return err @@ -277,12 +267,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) error { apiNames = append(apiNames, a) } - apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.stack) - if err != nil { - utils.Fatalf("Unable to determine supported api's: %v", err) - } - - jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client, f) + jeth := utils.NewJeth(js.re, js.client) js.re.Set("jeth", struct{}{}) t, _ := js.re.Get("jeth") jethObj := t.Object() @@ -313,14 +298,16 @@ func (js *jsre) apiBindings(f xeth.Frontend) error { // load only supported API's in javascript runtime shortcuts := "var eth = web3.eth; " for _, apiName := range apiNames { - if apiName == shared.Web3ApiName { - continue // manually mapped + if apiName == "web3" || apiName == "rpc" { + continue // manually mapped or ignore } - if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), api.Javascript(apiName)); err == nil { - shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName) - } else { - utils.Fatalf("Error loading %s.js: %v", apiName, err) + if jsFile, ok := rpc.WEB3Extensions[apiName]; ok { + if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), jsFile); err == nil { + shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName) + } else { + utils.Fatalf("Error loading %s.js: %v", apiName, err) + } } } @@ -375,14 +362,13 @@ func (self *jsre) ConfirmTransaction(tx string) bool { return false } // If natspec is enabled, ask for permission - if ethereum.NatSpec { - notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient()) - fmt.Println(notice) - answer, _ := self.Prompt("Confirm Transaction [y/n]") - return strings.HasPrefix(strings.Trim(answer, " "), "y") - } else { - return true + if ethereum.NatSpec && false /* disabled for now */ { + // notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient()) + // fmt.Println(notice) + // answer, _ := self.Prompt("Confirm Transaction [y/n]") + // return strings.HasPrefix(strings.Trim(answer, " "), "y") } + return true } func (self *jsre) UnlockAccount(addr []byte) bool { diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index ca636188f..19583c5ef 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -32,30 +32,27 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/httpclient" - "github.com/ethereum/go-ethereum/common/natspec" - "github.com/ethereum/go-ethereum/common/registrar" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" + "github.com/ethereum/go-ethereum/cmd/utils" ) const ( testSolcPath = "" - solcVersion = "0.9.23" + solcVersion = "0.9.23" - testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674" + testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674" testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" testBalance = "10000000000000000000" - // of empty string +// of empty string testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" ) var ( - versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`)) + versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`)) testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")) testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}` ) @@ -77,15 +74,16 @@ func (self *testjethre) UnlockAccount(acc []byte) bool { return true } -func (self *testjethre) ConfirmTransaction(tx string) bool { - var ethereum *eth.Ethereum - self.stack.Service(ðereum) - - if ethereum.NatSpec { - self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.client) - } - return true -} +// Temporary disabled while natspec hasn't been migrated +//func (self *testjethre) ConfirmTransaction(tx string) bool { +// var ethereum *eth.Ethereum +// self.stack.Service(ðereum) +// +// if ethereum.NatSpec { +// self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.client) +// } +// return true +//} func testJEthRE(t *testing.T) (string, *testjethre, *node.Node) { return testREPL(t, nil) @@ -118,7 +116,9 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod if config != nil { config(ethConf) } - if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil { + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return eth.New(ctx, ethConf) + }); err != nil { t.Fatalf("failed to register ethereum protocol: %v", err) } // Initialize all the keys for testing @@ -141,9 +141,10 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod stack.Service(ðereum) assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") - client := comms.NewInProcClient(codec.JSON) + //client := comms.NewInProcClient(codec.JSON) + client := utils.NewInProcRPCClient(stack) tf := &testjethre{client: ethereum.HTTPClient()} - repl := newJSRE(stack, assetPath, "", client, false, tf) + repl := newJSRE(stack, assetPath, "", client, false) tf.jsre = repl return tmp, tf, stack } @@ -166,8 +167,8 @@ func TestAccounts(t *testing.T) { defer node.Stop() defer os.RemoveAll(tmp) - checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`) - checkEvalJSON(t, repl, `eth.coinbase`, `"`+testAddress+`"`) + checkEvalJSON(t, repl, `eth.accounts`, `["` + testAddress + `"]`) + checkEvalJSON(t, repl, `eth.coinbase`, `"` + testAddress + `"`) val, err := repl.re.Run(`jeth.newAccount("password")`) if err != nil { t.Errorf("expected no error, got %v", err) @@ -177,7 +178,7 @@ func TestAccounts(t *testing.T) { t.Errorf("address not hex: %q", addr) } - checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`","`+addr+`"]`) + checkEvalJSON(t, repl, `eth.accounts`, `["` + testAddress + `","` + addr + `"]`) } @@ -205,13 +206,13 @@ func TestBlockChain(t *testing.T) { node.Service(ðereum) ethereum.BlockChain().Reset() - checkEvalJSON(t, repl, `admin.exportChain(`+tmpfileq+`)`, `true`) + checkEvalJSON(t, repl, `admin.exportChain(` + tmpfileq + `)`, `true`) if _, err := os.Stat(tmpfile); err != nil { t.Fatal(err) } // check import, verify that dumpBlock gives the same result. - checkEvalJSON(t, repl, `admin.importChain(`+tmpfileq+`)`, `true`) + checkEvalJSON(t, repl, `admin.importChain(` + tmpfileq + `)`, `true`) checkEvalJSON(t, repl, `debug.dumpBlock(eth.blockNumber)`, beforeExport) } @@ -239,7 +240,7 @@ func TestCheckTestAccountBalance(t *testing.T) { defer os.RemoveAll(tmp) repl.re.Run(`primary = "` + testAddress + `"`) - checkEvalJSON(t, repl, `eth.getBalance(primary)`, `"`+testBalance+`"`) + checkEvalJSON(t, repl, `eth.getBalance(primary)`, `"` + testBalance + `"`) } func TestSignature(t *testing.T) { @@ -278,19 +279,20 @@ func TestContract(t *testing.T) { defer ethereum.Stop() defer os.RemoveAll(tmp) - reg := registrar.New(repl.xeth) - _, err := reg.SetGlobalRegistrar("", coinbase) - if err != nil { - t.Errorf("error setting HashReg: %v", err) - } - _, err = reg.SetHashReg("", coinbase) - if err != nil { - t.Errorf("error setting HashReg: %v", err) - } - _, err = reg.SetUrlHint("", coinbase) - if err != nil { - t.Errorf("error setting HashReg: %v", err) - } + // Temporary disabled while registrar isn't migrated + //reg := registrar.New(repl.xeth) + //_, err := reg.SetGlobalRegistrar("", coinbase) + //if err != nil { + // t.Errorf("error setting HashReg: %v", err) + //} + //_, err = reg.SetHashReg("", coinbase) + //if err != nil { + // t.Errorf("error setting HashReg: %v", err) + //} + //_, err = reg.SetUrlHint("", coinbase) + //if err != nil { + // t.Errorf("error setting HashReg: %v", err) + //} /* TODO: * lookup receipt and contract addresses by tx hash * name registration for HashReg and UrlHint addresses @@ -299,11 +301,11 @@ func TestContract(t *testing.T) { */ source := `contract test {\n` + - " /// @notice Will multiply `a` by 7." + `\n` + - ` function multiply(uint a) returns(uint d) {\n` + - ` return a * 7;\n` + - ` }\n` + - `}\n` + " /// @notice Will multiply `a` by 7." + `\n` + + ` function multiply(uint a) returns(uint d) {\n` + + ` return a * 7;\n` + + ` }\n` + + `}\n` if checkEvalJSON(t, repl, `admin.stopNatSpec()`, `true`) != nil { return @@ -313,10 +315,10 @@ func TestContract(t *testing.T) { if err != nil { t.Fatalf("%v", err) } - if checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`) != nil { + if checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"` + testAddress + `"`) != nil { return } - if checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`) != nil { + if checkEvalJSON(t, repl, `source = "` + source + `"`, `"` + source + `"`) != nil { return } @@ -394,7 +396,7 @@ multiply7 = Multiply7.at(contractaddress); var contentHash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"` if sol != nil && solcVersion != sol.Version() { - modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`)) + modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"` + sol.Version() + `"`)) fmt.Printf("modified contractinfo:\n%s\n", modContractInfo) contentHash = `"` + common.ToHex(crypto.Sha3([]byte(modContractInfo))) + `"` } @@ -474,11 +476,12 @@ func processTxs(repl *testjethre, t *testing.T, expTxc int) bool { defer ethereum.StopMining() timer := time.NewTimer(100 * time.Second) - height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1)) + blockNr := ethereum.BlockChain().CurrentBlock().Number() + height := new(big.Int).Add(blockNr, big.NewInt(1)) repl.wait <- height select { case <-timer.C: - // if times out make sure the xeth loop does not block + // if times out make sure the xeth loop does not block go func() { select { case repl.wait <- nil: diff --git a/cmd/geth/main.go b/cmd/geth/main.go index f2bb27552..e6d190914 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -40,8 +40,6 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" ) const ( @@ -263,11 +261,11 @@ See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console Name: "attach", Usage: `Geth Console: interactive JavaScript environment (connect to node)`, Description: ` -The Geth console is an interactive shell for the JavaScript runtime environment -which exposes a node admin interface as well as the Ðapp JavaScript API. -See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console. -This command allows to open a console on a running geth node. -`, + The Geth console is an interactive shell for the JavaScript runtime environment + which exposes a node admin interface as well as the Ðapp JavaScript API. + See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console. + This command allows to open a console on a running geth node. + `, }, { Action: execScripts, @@ -309,11 +307,15 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.RPCEnabledFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, - utils.RpcApiFlag, + utils.RPCApiFlag, + utils.WSEnabledFlag, + utils.WSListenAddrFlag, + utils.WSPortFlag, + utils.WSApiFlag, + utils.WSAllowedDomainsFlag, utils.IPCDisabledFlag, utils.IPCApiFlag, utils.IPCPathFlag, - utils.IPCExperimental, utils.ExecFlag, utils.WhisperEnabledFlag, utils.DevModeFlag, @@ -392,20 +394,12 @@ func geth(ctx *cli.Context) { node.Wait() } +// attach will connect to a running geth instance attaching a JavaScript console and to it. func attach(ctx *cli.Context) { - var client comms.EthereumClient - var err error - if ctx.Args().Present() { - client, err = comms.ClientFromEndpoint(ctx.Args().First(), codec.JSON) - } else { - cfg := comms.IpcConfig{ - Endpoint: utils.IpcSocketPath(ctx), - } - client, err = comms.NewIpcClient(cfg, codec.JSON) - } - + // attach to a running geth instance + client, err := utils.NewRemoteRPCClient(ctx) if err != nil { - utils.Fatalf("Unable to attach to geth node - %v", err) + utils.Fatalf("Unable to attach to geth - %v", err) } repl := newLightweightJSRE( @@ -431,11 +425,12 @@ func console(ctx *cli.Context) { startNode(ctx, node) // Attach to the newly started node, and either execute script or become interactive - client := comms.NewInProcClient(codec.JSON) + client := utils.NewInProcRPCClient(node) + repl := newJSRE(node, ctx.GlobalString(utils.JSpathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), - client, true, nil) + client, true) if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { repl.batch(script) @@ -454,11 +449,12 @@ func execScripts(ctx *cli.Context) { startNode(ctx, node) // Attach to the newly started node and execute the given scripts - client := comms.NewInProcClient(codec.JSON) + client := utils.NewInProcRPCClient(node) + repl := newJSRE(node, ctx.GlobalString(utils.JSpathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), - client, false, nil) + client, false) for _, file := range ctx.Args() { repl.exec(file) @@ -517,6 +513,11 @@ func startNode(ctx *cli.Context, stack *node.Node) { utils.Fatalf("Failed to start RPC: %v", err) } } + if ctx.GlobalBool(utils.WSEnabledFlag.Name) { + if err := utils.StartWS(stack, ctx); err != nil { + utils.Fatalf("Failed to start WS: %v", err) + } + } if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil { utils.Fatalf("Failed to start mining: %v", err) diff --git a/cmd/geth/monitorcmd.go b/cmd/geth/monitorcmd.go index a45d29b8f..1d7bf3f6a 100644 --- a/cmd/geth/monitorcmd.go +++ b/cmd/geth/monitorcmd.go @@ -21,16 +21,15 @@ import ( "math" "reflect" "runtime" - "sort" "strings" "time" + "sort" + "github.com/codegangsta/cli" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" "github.com/gizak/termui" ) @@ -70,20 +69,18 @@ to display multiple metrics simultaneously. // monitor starts a terminal UI based monitoring tool for the requested metrics. func monitor(ctx *cli.Context) { var ( - client comms.EthereumClient + client rpc.Client err error ) // Attach to an Ethereum node over IPC or RPC endpoint := ctx.String(monitorCommandAttachFlag.Name) - if client, err = comms.ClientFromEndpoint(endpoint, codec.JSON); err != nil { + if client, err = utils.NewRemoteRPCClientFromString(endpoint); err != nil { utils.Fatalf("Unable to attach to geth node: %v", err) } defer client.Close() - xeth := rpc.NewXeth(client) - // Retrieve all the available metrics and resolve the user pattens - metrics, err := retrieveMetrics(xeth) + metrics, err := retrieveMetrics(client) if err != nil { utils.Fatalf("Failed to retrieve system metrics: %v", err) } @@ -133,7 +130,7 @@ func monitor(ctx *cli.Context) { } termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer))) - refreshCharts(xeth, monitored, data, units, charts, ctx, footer) + refreshCharts(client, monitored, data, units, charts, ctx, footer) termui.Body.Align() termui.Render(termui.Body) @@ -154,7 +151,7 @@ func monitor(ctx *cli.Context) { termui.Render(termui.Body) } case <-refresh: - if refreshCharts(xeth, monitored, data, units, charts, ctx, footer) { + if refreshCharts(client, monitored, data, units, charts, ctx, footer) { termui.Body.Align() } termui.Render(termui.Body) @@ -164,8 +161,30 @@ func monitor(ctx *cli.Context) { // retrieveMetrics contacts the attached geth node and retrieves the entire set // of collected system metrics. -func retrieveMetrics(xeth *rpc.Xeth) (map[string]interface{}, error) { - return xeth.Call("debug_metrics", []interface{}{true}) +func retrieveMetrics(client rpc.Client) (map[string]interface{}, error) { + req := map[string]interface{}{ + "id": new(int64), + "method": "debug_metrics", + "jsonrpc": "2.0", + "params": []interface{}{true}, + } + + if err := client.Send(req); err != nil { + return nil, err + } + + var res rpc.JSONSuccessResponse + if err := client.Recv(&res); err != nil { + return nil, err + } + + if res.Result != nil { + if mets, ok := res.Result.(map[string]interface{}); ok { + return mets, nil + } + } + + return nil, fmt.Errorf("unable to retrieve metrics") } // resolveMetrics takes a list of input metric patterns, and resolves each to one @@ -253,8 +272,8 @@ func fetchMetric(metrics map[string]interface{}, metric string) float64 { // refreshCharts retrieves a next batch of metrics, and inserts all the new // values into the active datasets and charts -func refreshCharts(xeth *rpc.Xeth, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) { - values, err := retrieveMetrics(xeth) +func refreshCharts(client rpc.Client, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) { + values, err := retrieveMetrics(client) for i, metric := range metrics { if len(data) < 512 { data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...) diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 7a6ff704c..a9fce6418 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -87,7 +87,12 @@ var AppHelpFlagGroups = []flagGroup{ utils.RPCEnabledFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, - utils.RpcApiFlag, + utils.RPCApiFlag, + utils.WSEnabledFlag, + utils.WSListenAddrFlag, + utils.WSPortFlag, + utils.WSApiFlag, + utils.WSAllowedDomainsFlag, utils.IPCDisabledFlag, utils.IPCApiFlag, utils.IPCPathFlag, @@ -158,7 +163,6 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ utils.WhisperEnabledFlag, utils.NatspecEnabledFlag, - utils.IPCExperimental, }, }, { |