diff --git a/cmd/console/admin.go b/cmd/console/admin.go new file mode 100644 index 0000000000..2fe5d02b43 --- /dev/null +++ b/cmd/console/admin.go @@ -0,0 +1,843 @@ +package main + +/* +node admin bindings +*/ + +func (js *jsre) adminBindings() { + /* + ethO, _ := js.re.Get("eth") + eth := ethO.Object() + eth.Set("pendingTransactions", js.pendingTransactions) + eth.Set("resend", js.resend) + eth.Set("sign", js.sign) + + js.re.Set("admin", struct{}{}) + t, _ := js.re.Get("admin") + admin := t.Object() + admin.Set("addPeer", js.addPeer) + admin.Set("startRPC", js.startRPC) + admin.Set("stopRPC", js.stopRPC) + admin.Set("nodeInfo", js.nodeInfo) + admin.Set("peers", js.peers) + admin.Set("newAccount", js.newAccount) + admin.Set("unlock", js.unlock) + admin.Set("import", js.importChain) + admin.Set("export", js.exportChain) + admin.Set("verbosity", js.verbosity) + admin.Set("progress", js.downloadProgress) + admin.Set("setSolc", js.setSolc) + + admin.Set("contractInfo", struct{}{}) + t, _ = admin.Get("contractInfo") + cinfo := t.Object() + // newRegistry officially not documented temporary option + cinfo.Set("start", js.startNatSpec) + cinfo.Set("stop", js.stopNatSpec) + cinfo.Set("newRegistry", js.newRegistry) + cinfo.Set("get", js.getContractInfo) + cinfo.Set("register", js.register) + cinfo.Set("registerUrl", js.registerUrl) + // cinfo.Set("verify", js.verify) + + admin.Set("miner", struct{}{}) + t, _ = admin.Get("miner") + miner := t.Object() + miner.Set("start", js.startMining) + miner.Set("stop", js.stopMining) + miner.Set("hashrate", js.hashrate) + miner.Set("setExtra", js.setExtra) + miner.Set("setGasPrice", js.setGasPrice) + miner.Set("startAutoDAG", js.startAutoDAG) + miner.Set("stopAutoDAG", js.stopAutoDAG) + miner.Set("makeDAG", js.makeDAG) + + admin.Set("debug", struct{}{}) + t, _ = admin.Get("debug") + debug := t.Object() + js.re.Set("sleep", js.sleep) + debug.Set("backtrace", js.backtrace) + debug.Set("printBlock", js.printBlock) + debug.Set("dumpBlock", js.dumpBlock) + debug.Set("getBlockRlp", js.getBlockRlp) + debug.Set("setHead", js.setHead) + debug.Set("processBlock", js.debugBlock) + debug.Set("seedhash", js.seedHash) + // undocumented temporary + debug.Set("waitForBlocks", js.waitForBlocks) + */ +} + +/* +// generic helper to getBlock by Number/Height or Hex depending on autodetected input +// if argument is missing the current block is returned +// if block is not found or there is problem with decoding +// the appropriate value is returned and block is guaranteed to be nil +func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) { + var block *types.Block + if len(call.ArgumentList) > 0 { + if call.Argument(0).IsNumber() { + num, _ := call.Argument(0).ToInteger() + block = js.ethereum.ChainManager().GetBlockByNumber(uint64(num)) + } else if call.Argument(0).IsString() { + hash, _ := call.Argument(0).ToString() + block = js.ethereum.ChainManager().GetBlock(common.HexToHash(hash)) + } else { + return nil, errors.New("invalid argument for dump. Either hex string or number") + } + } else { + block = js.ethereum.ChainManager().CurrentBlock() + } + + if block == nil { + return nil, errors.New("block not found") + } + return block, nil +} + +func (js *jsre) seedHash(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) > 0 { + if call.Argument(0).IsNumber() { + num, _ := call.Argument(0).ToInteger() + hash, err := ethash.GetSeedHash(uint64(num)) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + v, _ := call.Otto.ToValue(fmt.Sprintf("0x%x", hash)) + return v + } else { + fmt.Println("arg not a number") + } + } else { + fmt.Println("requires number argument") + } + + return otto.UndefinedValue() +} + +func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value { + txs := js.ethereum.TxPool().GetTransactions() + + // grab the accounts from the account manager. This will help with determening which + // transactions should be returned. + accounts, err := js.ethereum.AccountManager().Accounts() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + // Add the accouns to a new set + accountSet := set.New() + for _, account := range accounts { + accountSet.Add(account.Address) + } + + //ltxs := make([]*tx, len(txs)) + var ltxs []*tx + for _, tx := range txs { + // no need to check err + if from, _ := tx.From(); accountSet.Has(from) { + ltxs = append(ltxs, newTx(tx)) + } + } + + v, _ := call.Otto.ToValue(ltxs) + return v +} + +func (js *jsre) resend(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) == 0 { + fmt.Println("first argument must be a transaction") + return otto.FalseValue() + } + + v, err := call.Argument(0).Export() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + if tx, ok := v.(*tx); ok { + gl, gp := tx.GasLimit, tx.GasPrice + if len(call.ArgumentList) > 1 { + gp = call.Argument(1).String() + } + if len(call.ArgumentList) > 2 { + gl = call.Argument(2).String() + } + + ret, err := js.xeth.Transact(tx.From, tx.To, tx.Nonce, tx.Value, gl, gp, tx.Data) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + js.ethereum.TxPool().RemoveTransactions(types.Transactions{tx.tx}) + + v, _ := call.Otto.ToValue(ret) + return v + } + + fmt.Println("first argument must be a transaction") + return otto.FalseValue() +} + +func (js *jsre) sign(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 2 { + fmt.Println("requires 2 arguments: eth.sign(signer, data)") + return otto.UndefinedValue() + } + signer, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + data, err := call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + signed, err := js.xeth.Sign(signer, data, false) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + v, _ := call.Otto.ToValue(signed) + return v +} + +func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + old := vm.Debug + vm.Debug = true + _, err = js.ethereum.BlockProcessor().RetryProcess(block) + if err != nil { + fmt.Println(err) + } + vm.Debug = old + + fmt.Println("ok") + return otto.UndefinedValue() +} + +func (js *jsre) setHead(call otto.FunctionCall) otto.Value { + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + js.ethereum.ChainManager().SetHead(block) + return otto.UndefinedValue() +} + +func (js *jsre) downloadProgress(call otto.FunctionCall) otto.Value { + pending, cached := js.ethereum.Downloader().Stats() + v, _ := call.Otto.ToValue(map[string]interface{}{"pending": pending, "cached": cached}) + return v +} + +func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value { + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + encoded, _ := rlp.EncodeToBytes(block) + v, _ := call.Otto.ToValue(fmt.Sprintf("%x", encoded)) + return v +} + +func (js *jsre) setExtra(call otto.FunctionCall) otto.Value { + extra, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + if len(extra) > 1024 { + fmt.Println("error: cannot exceed 1024 bytes") + return otto.UndefinedValue() + } + + js.ethereum.Miner().SetExtra([]byte(extra)) + return otto.UndefinedValue() +} + +func (js *jsre) setGasPrice(call otto.FunctionCall) otto.Value { + gasPrice, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + js.ethereum.Miner().SetGasPrice(common.String2Big(gasPrice)) + return otto.UndefinedValue() +} + +func (js *jsre) hashrate(call otto.FunctionCall) otto.Value { + v, _ := call.Otto.ToValue(js.ethereum.Miner().HashRate()) + return v +} + +func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value { + blockNumber, err := call.Argument(1).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + err = ethash.MakeDAG(uint64(blockNumber), "") + if err != nil { + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (js *jsre) startAutoDAG(otto.FunctionCall) otto.Value { + js.ethereum.StartAutoDAG() + return otto.TrueValue() +} + +func (js *jsre) stopAutoDAG(otto.FunctionCall) otto.Value { + js.ethereum.StopAutoDAG() + return otto.TrueValue() +} + +func (js *jsre) backtrace(call otto.FunctionCall) otto.Value { + tracestr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + glog.GetTraceLocation().Set(tracestr) + + return otto.UndefinedValue() +} + +func (js *jsre) verbosity(call otto.FunctionCall) otto.Value { + v, err := call.Argument(0).ToInteger() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + glog.SetV(int(v)) + return otto.UndefinedValue() +} + +func (js *jsre) startMining(call otto.FunctionCall) otto.Value { + var ( + threads int64 + err error + ) + + if len(call.ArgumentList) > 0 { + threads, err = call.Argument(0).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + } else { + threads = int64(js.ethereum.MinerThreads) + } + + // switch on DAG autogeneration when miner starts + js.ethereum.StartAutoDAG() + + err = js.ethereum.StartMining(int(threads)) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + return otto.TrueValue() +} + +func (js *jsre) stopMining(call otto.FunctionCall) otto.Value { + js.ethereum.StopMining() + js.ethereum.StopAutoDAG() + return otto.TrueValue() +} + +func (js *jsre) startRPC(call otto.FunctionCall) otto.Value { + addr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + port, err := call.Argument(1).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + corsDomain := js.corsDomain + if len(call.ArgumentList) > 2 { + corsDomain, err = call.Argument(2).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + } + + config := rpc.RpcConfig{ + ListenAddress: addr, + ListenPort: uint(port), + CorsDomain: corsDomain, + } + + xeth := xeth.New(js.ethereum, nil) + err = rpc.Start(xeth, config) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + return otto.TrueValue() +} + +func (js *jsre) stopRPC(call otto.FunctionCall) otto.Value { + if rpc.Stop() == nil { + return otto.TrueValue() + } + return otto.FalseValue() +} + +func (js *jsre) addPeer(call otto.FunctionCall) otto.Value { + nodeURL, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + err = js.ethereum.AddPeer(nodeURL) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (js *jsre) unlock(call otto.FunctionCall) otto.Value { + addr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + seconds, err := call.Argument(2).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + if seconds == 0 { + seconds = accounts.DefaultAccountUnlockDuration + } + + arg := call.Argument(1) + var passphrase string + if arg.IsUndefined() { + fmt.Println("Please enter a passphrase now.") + passphrase, err = utils.PromptPassword("Passphrase: ", true) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + } else { + passphrase, err = arg.ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + } + am := js.ethereum.AccountManager() + err = am.TimedUnlock(common.HexToAddress(addr), passphrase, time.Duration(seconds)*time.Second) + if err != nil { + fmt.Printf("Unlock account failed '%v'\n", err) + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (js *jsre) newAccount(call otto.FunctionCall) otto.Value { + arg := call.Argument(0) + var passphrase string + if arg.IsUndefined() { + fmt.Println("The new account will be encrypted with a passphrase.") + fmt.Println("Please enter a passphrase now.") + auth, err := utils.PromptPassword("Passphrase: ", true) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + confirm, err := utils.PromptPassword("Repeat Passphrase: ", false) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + if auth != confirm { + fmt.Println("Passphrases did not match.") + return otto.FalseValue() + } + passphrase = auth + } else { + var err error + passphrase, err = arg.ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + } + acct, err := js.ethereum.AccountManager().NewAccount(passphrase) + if err != nil { + fmt.Printf("Could not create the account: %v", err) + return otto.UndefinedValue() + } + v, _ := call.Otto.ToValue(acct.Address.Hex()) + return v +} + +func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value { + v, _ := call.Otto.ToValue(js.ethereum.NodeInfo()) + return v +} + +func (js *jsre) peers(call otto.FunctionCall) otto.Value { + v, _ := call.Otto.ToValue(js.ethereum.PeersInfo()) + return v +} + +func (js *jsre) importChain(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) == 0 { + fmt.Println("require file name. admin.importChain(filename)") + return otto.FalseValue() + } + fn, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + if err := utils.ImportChain(js.ethereum.ChainManager(), fn); err != nil { + fmt.Println("Import error: ", err) + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (js *jsre) exportChain(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) == 0 { + fmt.Println("require file name: admin.exportChain(filename)") + return otto.FalseValue() + } + + fn, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + if err := utils.ExportChain(js.ethereum.ChainManager(), fn); err != nil { + fmt.Println(err) + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (js *jsre) printBlock(call otto.FunctionCall) otto.Value { + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + fmt.Println(block) + + return otto.UndefinedValue() +} + +func (js *jsre) dumpBlock(call otto.FunctionCall) otto.Value { + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + statedb := state.New(block.Root(), js.ethereum.StateDb()) + dump := statedb.RawDump() + v, _ := call.Otto.ToValue(dump) + return v +} + +func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) > 2 { + fmt.Println("requires 0, 1 or 2 arguments: admin.debug.waitForBlock(minHeight, timeout)") + return otto.FalseValue() + } + var n, timeout int64 + var timer <-chan time.Time + var height *big.Int + var err error + args := len(call.ArgumentList) + if args == 2 { + timeout, err = call.Argument(1).ToInteger() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + timer = time.NewTimer(time.Duration(timeout) * time.Second).C + } + if args >= 1 { + n, err = call.Argument(0).ToInteger() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + height = big.NewInt(n) + } + + if args == 0 { + height = js.xeth.CurrentBlock().Number() + height.Add(height, common.Big1) + } + + wait := js.wait + js.wait <- height + select { + case <-timer: + // if times out make sure the xeth loop does not block + go func() { + select { + case wait <- nil: + case <-wait: + } + }() + return otto.UndefinedValue() + case height = <-wait: + } + v, _ := call.Otto.ToValue(height.Uint64()) + return v +} + +func (js *jsre) sleep(call otto.FunctionCall) otto.Value { + sec, err := call.Argument(0).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + time.Sleep(time.Duration(sec) * time.Second) + return otto.UndefinedValue() +} + +func (js *jsre) setSolc(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 1 { + fmt.Println("needs 1 argument: admin.contractInfo.setSolc(solcPath)") + return otto.FalseValue() + } + solcPath, err := call.Argument(0).ToString() + if err != nil { + return otto.FalseValue() + } + solc, err := js.xeth.SetSolc(solcPath) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + fmt.Println(solc.Info()) + return otto.TrueValue() +} + +func (js *jsre) register(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 4 { + fmt.Println("requires 4 arguments: admin.contractInfo.register(fromaddress, contractaddress, contract, filename)") + return otto.UndefinedValue() + } + sender, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + address, err := call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + raw, err := call.Argument(2).Export() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + jsonraw, err := json.Marshal(raw) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + var contract compiler.Contract + err = json.Unmarshal(jsonraw, &contract) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + filename, err := call.Argument(3).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + contenthash, err := compiler.ExtractInfo(&contract, filename) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + // sender and contract address are passed as hex strings + codeb := js.xeth.CodeAtBytes(address) + codehash := common.BytesToHash(crypto.Sha3(codeb)) + + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + registry := resolver.New(js.xeth) + + _, err = registry.RegisterContentHash(common.HexToAddress(sender), codehash, contenthash) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + v, _ := call.Otto.ToValue(contenthash.Hex()) + return v +} + +func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 3 { + fmt.Println("requires 3 arguments: admin.contractInfo.register(fromaddress, contenthash, filename)") + return otto.FalseValue() + } + sender, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + contenthash, err := call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + url, err := call.Argument(2).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + registry := resolver.New(js.xeth) + + _, err = registry.RegisterUrl(common.HexToAddress(sender), common.HexToHash(contenthash), url) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + return otto.TrueValue() +} + +func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 1 { + fmt.Println("requires 1 argument: admin.contractInfo.register(contractaddress)") + return otto.FalseValue() + } + addr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + infoDoc, err := natspec.FetchDocsForContract(addr, js.xeth, ds) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + var info compiler.ContractInfo + err = json.Unmarshal(infoDoc, &info) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + v, _ := call.Otto.ToValue(info) + return v +} + +func (js *jsre) startNatSpec(call otto.FunctionCall) otto.Value { + js.ethereum.NatSpec = true + return otto.TrueValue() +} + +func (js *jsre) stopNatSpec(call otto.FunctionCall) otto.Value { + js.ethereum.NatSpec = false + return otto.TrueValue() +} + +func (js *jsre) newRegistry(call otto.FunctionCall) otto.Value { + + if len(call.ArgumentList) != 1 { + fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)") + return otto.FalseValue() + } + addr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + registry := resolver.New(js.xeth) + err = registry.CreateContracts(common.HexToAddress(addr)) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + return otto.TrueValue() +} + +// internal transaction type which will allow us to resend transactions using `eth.resend` +type tx struct { + tx *types.Transaction + + To string + From string + Nonce string + Value string + Data string + GasLimit string + GasPrice string +} + +func newTx(t *types.Transaction) *tx { + from, _ := t.From() + var to string + if t := t.To(); t != nil { + to = t.Hex() + } + + return &tx{ + tx: t, + To: to, + From: from.Hex(), + Value: t.Amount.String(), + Nonce: strconv.Itoa(int(t.Nonce())), + Data: "0x" + common.Bytes2Hex(t.Data()), + GasLimit: t.GasLimit.String(), + GasPrice: t.GasPrice().String(), + } +} +*/ diff --git a/cmd/console/js.go b/cmd/console/js.go index f2f74f75bf..430970e8e7 100644 --- a/cmd/console/js.go +++ b/cmd/console/js.go @@ -74,7 +74,7 @@ func newJSRE(datadir, libPath, ipcpath string) *jsre { // update state in separare forever blocks js.re = re.New(libPath) js.apiBindings(ipcpath) - // TODO js.adminBindings() + js.adminBindings() if !liner.TerminalSupported() { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} @@ -173,6 +173,13 @@ func (self *jsre) exec(filename string) error { return nil } +// show summary of current geth instance +func (self *jsre) welcome() { + self.re.Eval(` + console.log('Connected to ' + web3.version.client); + `) +} + func (self *jsre) interactive() { // Read input lines. prompt := make(chan string) diff --git a/cmd/console/main.go b/cmd/console/main.go index 836492738b..5c46a24310 100644 --- a/cmd/console/main.go +++ b/cmd/console/main.go @@ -100,6 +100,6 @@ func run(ctx *cli.Context) { ipcpath := ctx.GlobalString(utils.IPCPathFlag.Name) repl := newJSRE(datadir, jspath, ipcpath) - + repl.welcome() repl.interactive() } diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index dee25e44e6..213caacfe4 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -105,7 +105,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { t.Errorf("Error creating DocServer: %v", err) } tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()} - repl := newJSRE(ethereum, assetPath, "", false, tf) + repl := newJSRE(ethereum, assetPath, "", "", false, tf) tf.jsre = repl return tmp, tf, ethereum } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index d092826bc0..a355d60084 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -305,7 +305,7 @@ func console(ctx *cli.Context) { repl := newJSRE( ethereum, ctx.String(utils.JSpathFlag.Name), - ctx.String(utils.IPCPathFlag.Name), + ctx.GlobalString(utils.IPCPathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), true, nil, diff --git a/rpc/api/eth.go b/rpc/api/eth.go index 6ed8d9826c..b633f9991e 100644 --- a/rpc/api/eth.go +++ b/rpc/api/eth.go @@ -546,40 +546,252 @@ type GetBalanceArgs struct { BlockNumber int64 } +func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + type GetStorageArgs struct { Address string BlockNumber int64 } +func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + type GetStorageAtArgs struct { Address string BlockNumber int64 Key string } +func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + keystr, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("key", "not a string") + } + args.Key = keystr + + if len(obj) > 2 { + if err := blockHeight(obj[2], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + type GetTxCountArgs struct { Address string BlockNumber int64 } +func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + type HashArgs struct { Hash string } +func (args *HashArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + arg0, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("hash", "not a string") + } + args.Hash = arg0 + + return nil +} + type BlockNumArg struct { BlockNumber int64 } +func (args *BlockNumArg) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + if err := blockHeight(obj[0], &args.BlockNumber); err != nil { + return err + } + + return nil +} + type GetDataArgs struct { Address string BlockNumber int64 } +func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + type NewSignArgs struct { From string Data string } +func (args *NewSignArgs) UnmarshalJSON(b []byte) (err error) { + var obj []json.RawMessage + var ext struct { + From string + Data string + } + + // Decode byte slice to array of RawMessages + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + // Check for sufficient params + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + // Decode 0th RawMessage to temporary struct + if err := json.Unmarshal(obj[0], &ext); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(ext.From) == 0 { + return shared.NewValidationError("from", "is required") + } + + if len(ext.Data) == 0 { + return shared.NewValidationError("data", "is required") + } + + args.From = ext.From + args.Data = ext.Data + return nil +} + type NewTxArgs struct { From string To string @@ -592,36 +804,12 @@ type NewTxArgs struct { BlockNumber int64 } -type SourceArgs struct { - Source string -} - -type CallArgs struct { - From string - To string - Value *big.Int - Gas *big.Int - GasPrice *big.Int - Data string - - BlockNumber int64 -} - -type HashIndexArgs struct { - Hash string - Index int64 -} - -type BlockNumIndexArgs struct { - BlockNumber int64 - Index int64 -} - func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { var obj []json.RawMessage var ext struct { From string To string + Nonce interface{} Value interface{} Gas interface{} GasPrice interface{} @@ -652,6 +840,14 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { args.Data = ext.Data var num *big.Int + if ext.Nonce != nil { + num, err = numString(ext.Nonce) + if err != nil { + return err + } + } + args.Nonce = num + if ext.Value == nil { num = big.NewInt(0) } else { @@ -694,6 +890,173 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { return nil } +type SourceArgs struct { + Source string +} + +func (args *SourceArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + arg0, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("source code", "not a string") + } + args.Source = arg0 + + return nil +} + +type CallArgs struct { + From string + To string + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data string + + BlockNumber int64 +} + +func (args *CallArgs) UnmarshalJSON(b []byte) (err error) { + var obj []json.RawMessage + var ext struct { + From string + To string + Value interface{} + Gas interface{} + GasPrice interface{} + Data string + } + + // Decode byte slice to array of RawMessages + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + // Check for sufficient params + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + // Decode 0th RawMessage to temporary struct + if err := json.Unmarshal(obj[0], &ext); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + args.From = ext.From + + if len(ext.To) == 0 { + return shared.NewValidationError("to", "is required") + } + args.To = ext.To + + var num *big.Int + if ext.Value == nil { + num = big.NewInt(0) + } else { + if num, err = numString(ext.Value); err != nil { + return err + } + } + args.Value = num + + if ext.Gas == nil { + num = big.NewInt(0) + } else { + if num, err = numString(ext.Gas); err != nil { + return err + } + } + args.Gas = num + + if ext.GasPrice == nil { + num = big.NewInt(0) + } else { + if num, err = numString(ext.GasPrice); err != nil { + return err + } + } + args.GasPrice = num + + args.Data = ext.Data + + // Check for optional BlockNumber param + if len(obj) > 1 { + if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type HashIndexArgs struct { + Hash string + Index int64 +} + +func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + arg0, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("hash", "not a string") + } + args.Hash = arg0 + + arg1, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("index", "not a string") + } + args.Index = common.Big(arg1).Int64() + + return nil +} + +type BlockNumIndexArgs struct { + BlockNumber int64 + Index int64 +} + +func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + if err := blockHeight(obj[0], &args.BlockNumber); err != nil { + return err + } + + var arg1 *big.Int + if arg1, err = numString(obj[1]); err != nil { + return err + } + args.Index = arg1.Int64() + + return nil +} + type GetBlockByHashArgs struct { BlockHash string IncludeTxs bool diff --git a/rpc/codec/json.go b/rpc/codec/json.go index cc3057fe9b..31024ee746 100644 --- a/rpc/codec/json.go +++ b/rpc/codec/json.go @@ -7,6 +7,10 @@ import ( "github.com/ethereum/go-ethereum/rpc/shared" ) +const ( + MAX_RESPONSE_SIZE = 64 * 1024 +) + // Json serialization support type JsonCodec struct { c net.Conn @@ -35,16 +39,19 @@ func (self *JsonCodec) ReadRequest() (*shared.Request, error) { func (self *JsonCodec) ReadResponse() (interface{}, error) { var err error - var success shared.SuccessResponse - if err = self.d.Decode(&success); err == nil { - return success, nil - } + buf := make([]byte, MAX_RESPONSE_SIZE) + n, _ := self.c.Read(buf) var failure shared.ErrorResponse - if err = self.d.Decode(&failure); err == nil { + if err = json.Unmarshal(buf[:n], &failure); err == nil && failure.Error != nil { return failure, nil } + var success shared.SuccessResponse + if err = json.Unmarshal(buf[:n], &success); err == nil { + return success, nil + } + return nil, err } diff --git a/rpc/jeth.go b/rpc/jeth.go index 9c13998551..5e6dcca3bd 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -40,10 +40,10 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { if err != nil { return self.err(call, -32700, err.Error(), nil) } - + client, err := comms.NewIpcClient(comms.IpcConfig{self.ipcpath}, codec.JSON) if err != nil { - fmt.Println("Error response:", err) + fmt.Println("Unable to connect to geth.") return self.err(call, -32603, err.Error(), -1) } defer client.Close() @@ -80,19 +80,23 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { resObj, _ := json.Marshal(res.Result) call.Otto.Set("ret_result", string(resObj)) call.Otto.Set("response_idx", i) + + response, err = call.Otto.Run(` + ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; + `) } else if res, ok := respif.(shared.ErrorResponse); ok { call.Otto.Set("ret_id", res.Id) call.Otto.Set("ret_jsonrpc", res.Jsonrpc) - errorObj, _ := json.Marshal(res.Error) - call.Otto.Set("ret_result", string(errorObj)) + call.Otto.Set("ret_error", res.Error) call.Otto.Set("response_idx", i) - } else { - fmt.Printf("different type\n", reflect.TypeOf(respif)) - } - response, err = call.Otto.Run(` - ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; - `) + response, _ = call.Otto.Run(` + ret_response = { jsonrpc: ret_jsonrpc, id: ret_id, error: ret_error }; + `) + return + } else { + fmt.Printf("unexpected response\n", reflect.TypeOf(respif)) + } } if !batch {