From a4a4e9fcf824189d8d06940492a01effe6e6cf92 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Wed, 17 Jun 2015 16:22:35 +0200 Subject: [PATCH] removed old rpc structure and added new inproc api client --- cmd/geth/admin.go | 937 --------------------------- cmd/geth/js.go | 189 +++++- cmd/geth/main.go | 9 +- cmd/utils/flags.go | 4 +- core/transaction_pool.go | 2 +- rpc/api.go | 646 ------------------- rpc/api/api.go | 10 +- rpc/api/args.go | 57 ++ rpc/{ => api}/args_test.go | 11 +- rpc/api_test.go | 205 ------ rpc/args.go | 1230 ------------------------------------ rpc/comms/comms.go | 18 +- rpc/comms/http.go | 24 + rpc/comms/http_net.go | 14 +- rpc/comms/inproc.go | 53 ++ rpc/comms/ipc.go | 8 +- rpc/http.go | 163 ----- rpc/jeth.go | 44 +- rpc/responses.go | 316 --------- rpc/responses_test.go | 295 --------- rpc/types.go | 381 ----------- rpc/types_test.go | 204 ------ 22 files changed, 393 insertions(+), 4427 deletions(-) delete mode 100644 cmd/geth/admin.go delete mode 100644 rpc/api.go create mode 100644 rpc/api/args.go rename rpc/{ => api}/args_test.go (99%) delete mode 100644 rpc/api_test.go delete mode 100644 rpc/args.go create mode 100644 rpc/comms/inproc.go delete mode 100644 rpc/http.go delete mode 100644 rpc/responses.go delete mode 100644 rpc/responses_test.go delete mode 100644 rpc/types.go delete mode 100644 rpc/types_test.go diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go deleted file mode 100644 index 33ef69792..000000000 --- a/cmd/geth/admin.go +++ /dev/null @@ -1,937 +0,0 @@ -package main - -import ( - "encoding/json" - "errors" - "fmt" - "math/big" - "strconv" - "time" - - "github.com/ethereum/ethash" - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/compiler" - "github.com/ethereum/go-ethereum/common/natspec" - "github.com/ethereum/go-ethereum/common/resolver" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/xeth" - "github.com/robertkrimen/otto" - "gopkg.in/fatih/set.v0" -) - -/* -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.syncProgress) - 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("txPool", struct{}{}) - t, _ = admin.Get("txPool") - txPool := t.Object() - txPool.Set("pending", js.allPendingTransactions) - txPool.Set("queued", js.allQueuedTransactions) - - 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) - debug.Set("insertBlock", js.insertBlockRlp) - // 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) allPendingTransactions(call otto.FunctionCall) otto.Value { - txs := js.ethereum.TxPool().GetTransactions() - - ltxs := make([]*tx, len(txs)) - for i, tx := range txs { - // no need to check err - ltxs[i] = newTx(tx) - } - - v, _ := call.Otto.ToValue(ltxs) - return v -} - -func (js *jsre) allQueuedTransactions(call otto.FunctionCall) otto.Value { - txs := js.ethereum.TxPool().GetQueuedTransactions() - - ltxs := make([]*tx, len(txs)) - for i, tx := range txs { - // no need to check err - ltxs[i] = newTx(tx) - } - - v, _ := call.Otto.ToValue(ltxs) - return v -} - -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 { - 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() - } - - tstart := time.Now() - old := vm.Debug - - if len(call.ArgumentList) > 1 { - vm.Debug, _ = call.Argument(1).ToBoolean() - } - - _, err = js.ethereum.BlockProcessor().RetryProcess(block) - if err != nil { - fmt.Println(err) - r, _ := call.Otto.ToValue(map[string]interface{}{"success": false, "time": time.Since(tstart).Seconds()}) - return r - } - vm.Debug = old - - r, _ := call.Otto.ToValue(map[string]interface{}{"success": true, "time": time.Since(tstart).Seconds()}) - return r -} - -func (js *jsre) insertBlockRlp(call otto.FunctionCall) otto.Value { - tstart := time.Now() - - var block types.Block - if call.Argument(0).IsString() { - blockRlp, _ := call.Argument(0).ToString() - err := rlp.DecodeBytes(common.Hex2Bytes(blockRlp), &block) - 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) - r, _ := call.Otto.ToValue(map[string]interface{}{"success": false, "time": time.Since(tstart).Seconds()}) - return r - } - vm.Debug = old - - r, _ := call.Otto.ToValue(map[string]interface{}{"success": true, "time": time.Since(tstart).Seconds()}) - return r -} - -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) syncProgress(call otto.FunctionCall) otto.Value { - pending, cached, importing, eta := js.ethereum.Downloader().Stats() - v, _ := call.Otto.ToValue(map[string]interface{}{ - "pending": pending, - "cached": cached, - "importing": importing, - "estimate": (eta / time.Second * time.Second).String(), - }) - 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/geth/js.go b/cmd/geth/js.go index 7e6e10ca9..2ae4817b3 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -33,6 +33,10 @@ import ( "github.com/ethereum/go-ethereum/eth" re "github.com/ethereum/go-ethereum/jsre" "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" @@ -70,10 +74,70 @@ type jsre struct { ps1 string atexit func() corsDomain string + client comms.EthereumClient prompter } -func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, interactive bool, f xeth.Frontend) *jsre { +var ( + loadedModulesMethods map[string][]string +) + +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) + } + } 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 { + return "", nil, "" + } + + 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 + } + + begin := line[:i] + keyword := line[i:pos] + end := line[pos:] + + completionWords := keywordCompleter(keyword) + return begin, completionWords, end +} + +func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre { js := &jsre{ethereum: ethereum, ps1: "> "} // set default cors domain used by startRpc from CLI flag js.corsDomain = corsDomain @@ -82,10 +146,16 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera } js.xeth = xeth.New(ethereum, f) js.wait = js.xeth.UpdateState() + js.client = client + if clt, ok := js.client.(*comms.InProcClient); ok { + clt.Initialize(js.xeth, ethereum) + } + // update state in separare forever blocks js.re = re.New(libPath) - js.apiBindings(ipcpath, f) - js.adminBindings() + if err := js.apiBindings(f); err != nil { + utils.Fatalf("Unable to connect - %v", err) + } if !liner.TerminalSupported() || !interactive { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} @@ -93,6 +163,9 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera lr := liner.NewLiner() js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) lr.SetCtrlCAborts(true) + js.loadAutoCompletion() + lr.SetWordCompleter(apiWordCompleter) + lr.SetTabCompletionStyle(liner.TabPrints) js.prompter = lr js.atexit = func() { js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) @@ -103,8 +176,117 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera return js } +func (self *jsre) loadAutoCompletion() { + if modules, err := self.suportedApis(); err == nil { + loadedModulesMethods = make(map[string][]string) + for module, _ := range modules { + loadedModulesMethods[module] = api.AutoCompletion[module] + } + } +} + +func (self *jsre) suportedApis() (map[string]string, error) { + req := shared.Request{ + Id: 1, + Jsonrpc: "2.0", + Method: "modules", + } + + err := self.client.Send(&req) + if err != nil { + return nil, err + } + + res, err := self.client.Recv() + if err != nil { + return nil, err + } + + if sucRes, ok := res.(map[string]string); ok { + if err == nil { + return sucRes, nil + } + } + + return nil, fmt.Errorf("Unable to determine supported API's") +} + +func (js *jsre) apiBindings(f xeth.Frontend) error { + apis, err := js.suportedApis() + if err != nil { + return err + } + + apiNames := make([]string, 0, len(apis)) + for a, _ := range apis { + apiNames = append(apiNames, a) + } + + apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.ethereum) + if err != nil { + utils.Fatalf("Unable to determine supported api's: %v", err) + } + + jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client) + js.re.Set("jeth", struct{}{}) + t, _ := js.re.Get("jeth") + jethObj := t.Object() + + jethObj.Set("send", jeth.Send) + jethObj.Set("sendAsync", jeth.Send) + + err = js.re.Compile("bignumber.js", re.BigNumber_JS) + if err != nil { + utils.Fatalf("Error loading bignumber.js: %v", err) + } + + err = js.re.Compile("ethereum.js", re.Web3_JS) + if err != nil { + utils.Fatalf("Error loading web3.js: %v", err) + } + + _, err = js.re.Eval("var web3 = require('web3');") + if err != nil { + utils.Fatalf("Error requiring web3: %v", err) + } + + _, err = js.re.Eval("web3.setProvider(jeth)") + if err != nil { + utils.Fatalf("Error setting web3 provider: %v", err) + } + + // load only supported API's in javascript runtime + shortcuts := "var eth = web3.eth; " + for _, apiName := range apiNames { + if apiName == api.Web3ApiName || apiName == api.EthApiName { + continue // manually mapped + } + + 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) + } + } + + _, err = js.re.Eval(shortcuts) + + if err != nil { + utils.Fatalf("Error setting namespaces: %v", err) + } + + js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");") + return nil +} + +/* func (js *jsre) apiBindings(ipcpath string, f xeth.Frontend) { xe := xeth.New(js.ethereum, f) + apiNames, err := js.suportedApis(ipcpath) + if err != nil { + return + } + ethApi := rpc.NewEthereumApi(xe) jeth := rpc.NewJeth(ethApi, js.re, ipcpath) @@ -146,6 +328,7 @@ var net = web3.net; js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");") } +*/ var ds, _ = docserver.New("/") diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 43e45f6cc..5c4c33cea 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -38,6 +38,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/comms" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" ) @@ -310,12 +312,14 @@ func console(ctx *cli.Context) { utils.Fatalf("%v", err) } + client := comms.NewInProcClient(codec.JSON) + startEth(ctx, ethereum) repl := newJSRE( ethereum, ctx.String(utils.JSpathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), - utils.IpcSocketPath(ctx), + client, true, nil, ) @@ -332,12 +336,13 @@ func execJSFiles(ctx *cli.Context) { utils.Fatalf("%v", err) } + client := comms.NewInProcClient(codec.JSON) startEth(ctx, ethereum) repl := newJSRE( ethereum, ctx.String(utils.JSpathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), - utils.IpcSocketPath(ctx), + client, false, nil, ) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e0eefb3e3..092bc5511 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -211,7 +211,7 @@ var ( RpcApiFlag = cli.StringFlag{ Name: "rpcapi", Usage: "Specify the API's which are offered over the HTTP RPC interface", - Value: api.DefaultHttpRpcApis, + Value: comms.DefaultHttpRpcApis, } IPCDisabledFlag = cli.BoolFlag{ Name: "ipcdisable", @@ -220,7 +220,7 @@ var ( IPCApiFlag = cli.StringFlag{ Name: "ipcapi", Usage: "Specify the API's which are offered over the IPC interface", - Value: api.DefaultIpcApis, + Value: comms.DefaultIpcApis, } IPCPathFlag = DirectoryFlag{ Name: "ipcpath", diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 5ebe3576b..34a1733d7 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -149,7 +149,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return ErrInvalidSender } - // Make sure the account exist. Non existant accounts + // Make sure the account exist. Non existent accounts // haven't got funds and well therefor never pass. if !pool.currentState().HasAccount(from) { return ErrNonExistentAccount diff --git a/rpc/api.go b/rpc/api.go deleted file mode 100644 index a344a8294..000000000 --- a/rpc/api.go +++ /dev/null @@ -1,646 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/json" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/xeth" -) - -type EthereumApi struct { - eth *xeth.XEth -} - -func NewEthereumApi(xeth *xeth.XEth) *EthereumApi { - api := &EthereumApi{ - eth: xeth, - } - - return api -} - -func (api *EthereumApi) xeth() *xeth.XEth { - return api.eth -} - -func (api *EthereumApi) xethAtStateNum(num int64) *xeth.XEth { - return api.xeth().AtStateNum(num) -} - -func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { - // Spec at https://github.com/ethereum/wiki/wiki/JSON-RPC - glog.V(logger.Debug).Infof("%s %s", req.Method, req.Params) - - switch req.Method { - case "web3_sha3": - args := new(Sha3Args) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = common.ToHex(crypto.Sha3(common.FromHex(args.Data))) - case "web3_clientVersion": - *reply = api.xeth().ClientVersion() - case "net_version": - *reply = api.xeth().NetworkVersion() - case "net_listening": - *reply = api.xeth().IsListening() - case "net_peerCount": - *reply = newHexNum(api.xeth().PeerCount()) - case "eth_protocolVersion": - *reply = api.xeth().EthVersion() - case "eth_coinbase": - *reply = newHexData(api.xeth().Coinbase()) - case "eth_mining": - *reply = api.xeth().IsMining() - case "eth_gasPrice": - v := api.xeth().DefaultGasPrice() - *reply = newHexNum(v.Bytes()) - case "eth_accounts": - *reply = api.xeth().Accounts() - case "eth_blockNumber": - v := api.xeth().CurrentBlock().Number() - *reply = newHexNum(v.Bytes()) - case "eth_getBalance": - args := new(GetBalanceArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - *reply = api.xethAtStateNum(args.BlockNumber).BalanceAt(args.Address) - //v := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance() - //*reply = common.ToHex(v.Bytes()) - case "eth_getStorage", "eth_storageAt": - args := new(GetStorageArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - *reply = api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage() - case "eth_getStorageAt": - args := new(GetStorageAtArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - *reply = api.xethAtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key) - case "eth_getTransactionCount": - args := new(GetTxCountArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - count := api.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address) - *reply = newHexNum(big.NewInt(int64(count)).Bytes()) - case "eth_getBlockTransactionCountByHash": - args := new(HashArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false) - if block == nil { - *reply = nil - } else { - *reply = newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes()) - } - case "eth_getBlockTransactionCountByNumber": - args := new(BlockNumArg) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := NewBlockRes(api.xeth().EthBlockByNumber(args.BlockNumber), false) - if block == nil { - *reply = nil - break - } - - *reply = newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes()) - case "eth_getUncleCountByBlockHash": - args := new(HashArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := api.xeth().EthBlockByHash(args.Hash) - br := NewBlockRes(block, false) - if br == nil { - *reply = nil - break - } - - *reply = newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes()) - case "eth_getUncleCountByBlockNumber": - args := new(BlockNumArg) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := api.xeth().EthBlockByNumber(args.BlockNumber) - br := NewBlockRes(block, false) - if br == nil { - *reply = nil - break - } - - *reply = newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes()) - - case "eth_getData", "eth_getCode": - args := new(GetDataArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address) - *reply = newHexData(v) - - case "eth_sign": - args := new(NewSigArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - v, err := api.xeth().Sign(args.From, args.Data, false) - if err != nil { - return err - } - *reply = v - - case "eth_sendRawTransaction": - args := new(NewDataArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - v, err := api.xeth().PushTx(args.Data) - if err != nil { - return err - } - *reply = v - - case "eth_sendTransaction", "eth_transact": - args := new(NewTxArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - // nonce may be nil ("guess" mode) - var nonce string - if args.Nonce != nil { - nonce = args.Nonce.String() - } - - var gas string - if args.Gas == nil { - gas = "" - } else { - gas = args.Gas.String() - } - - var gasprice string - if args.GasPrice == nil { - gasprice = "" - } else { - gasprice = args.GasPrice.String() - } - - v, err := api.xeth().Transact(args.From, args.To, nonce, args.Value.String(), gas, gasprice, args.Data) - if err != nil { - return err - } - *reply = v - case "eth_estimateGas": - _, gas, err := api.doCall(req.Params) - if err != nil { - return err - } - - // TODO unwrap the parent method's ToHex call - if len(gas) == 0 { - *reply = newHexNum(0) - } else { - *reply = newHexNum(gas) - } - case "eth_call": - v, _, err := api.doCall(req.Params) - if err != nil { - return err - } - - // TODO unwrap the parent method's ToHex call - if v == "0x0" { - *reply = newHexData([]byte{}) - } else { - *reply = newHexData(common.FromHex(v)) - } - case "eth_flush": - return NewNotImplementedError(req.Method) - case "eth_getBlockByHash": - args := new(GetBlockByHashArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := api.xeth().EthBlockByHash(args.BlockHash) - br := NewBlockRes(block, args.IncludeTxs) - - *reply = br - case "eth_getBlockByNumber": - args := new(GetBlockByNumberArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := api.xeth().EthBlockByNumber(args.BlockNumber) - br := NewBlockRes(block, args.IncludeTxs) - // If request was for "pending", nil nonsensical fields - if args.BlockNumber == -2 { - br.BlockHash = nil - br.BlockNumber = nil - br.Miner = nil - br.Nonce = nil - br.LogsBloom = nil - } - *reply = br - case "eth_getTransactionByHash": - args := new(HashArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - tx, bhash, bnum, txi := api.xeth().EthTransactionByHash(args.Hash) - if tx != nil { - v := NewTransactionRes(tx) - // if the blockhash is 0, assume this is a pending transaction - if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 { - v.BlockHash = newHexData(bhash) - v.BlockNumber = newHexNum(bnum) - v.TxIndex = newHexNum(txi) - } - *reply = v - } - case "eth_getTransactionByBlockHashAndIndex": - args := new(HashIndexArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := api.xeth().EthBlockByHash(args.Hash) - br := NewBlockRes(block, true) - if br == nil { - *reply = nil - break - } - - if args.Index >= int64(len(br.Transactions)) || args.Index < 0 { - // return NewValidationError("Index", "does not exist") - *reply = nil - } else { - *reply = br.Transactions[args.Index] - } - case "eth_getTransactionByBlockNumberAndIndex": - args := new(BlockNumIndexArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := api.xeth().EthBlockByNumber(args.BlockNumber) - v := NewBlockRes(block, true) - if v == nil { - *reply = nil - break - } - - if args.Index >= int64(len(v.Transactions)) || args.Index < 0 { - // return NewValidationError("Index", "does not exist") - *reply = nil - } else { - *reply = v.Transactions[args.Index] - } - case "eth_getUncleByBlockHashAndIndex": - args := new(HashIndexArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - br := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false) - if br == nil { - *reply = nil - return nil - } - - if args.Index >= int64(len(br.Uncles)) || args.Index < 0 { - // return NewValidationError("Index", "does not exist") - *reply = nil - } else { - *reply = br.Uncles[args.Index] - } - case "eth_getUncleByBlockNumberAndIndex": - args := new(BlockNumIndexArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - block := api.xeth().EthBlockByNumber(args.BlockNumber) - v := NewBlockRes(block, true) - - if v == nil { - *reply = nil - return nil - } - - if args.Index >= int64(len(v.Uncles)) || args.Index < 0 { - // return NewValidationError("Index", "does not exist") - *reply = nil - } else { - *reply = v.Uncles[args.Index] - } - - case "eth_getCompilers": - var lang string - if solc, _ := api.xeth().Solc(); solc != nil { - lang = "Solidity" - } - c := []string{lang} - *reply = c - - case "eth_compileLLL", "eth_compileSerpent": - return NewNotImplementedError(req.Method) - - case "eth_compileSolidity": - solc, _ := api.xeth().Solc() - if solc == nil { - return NewNotAvailableError(req.Method, "solc (solidity compiler) not found") - } - - args := new(SourceArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - contracts, err := solc.Compile(args.Source) - if err != nil { - return err - } - *reply = contracts - - case "eth_newFilter": - args := new(BlockFilterArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - id := api.xeth().NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics) - *reply = newHexNum(big.NewInt(int64(id)).Bytes()) - - case "eth_newBlockFilter": - *reply = newHexNum(api.xeth().NewBlockFilter()) - case "eth_newPendingTransactionFilter": - *reply = newHexNum(api.xeth().NewTransactionFilter()) - case "eth_uninstallFilter": - args := new(FilterIdArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = api.xeth().UninstallFilter(args.Id) - case "eth_getFilterChanges": - args := new(FilterIdArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - switch api.xeth().GetFilterType(args.Id) { - case xeth.BlockFilterTy: - *reply = NewHashesRes(api.xeth().BlockFilterChanged(args.Id)) - case xeth.TransactionFilterTy: - *reply = NewHashesRes(api.xeth().TransactionFilterChanged(args.Id)) - case xeth.LogFilterTy: - *reply = NewLogsRes(api.xeth().LogFilterChanged(args.Id)) - default: - *reply = []string{} // reply empty string slice - } - case "eth_getFilterLogs": - args := new(FilterIdArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = NewLogsRes(api.xeth().Logs(args.Id)) - case "eth_getLogs": - args := new(BlockFilterArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = NewLogsRes(api.xeth().AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)) - case "eth_getWork": - api.xeth().SetMining(true, 0) - *reply = api.xeth().RemoteMining().GetWork() - case "eth_submitWork": - args := new(SubmitWorkArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = api.xeth().RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)) - case "db_putString": - args := new(DbArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - if err := args.requirements(); err != nil { - return err - } - - api.xeth().DbPut([]byte(args.Database+args.Key), args.Value) - - *reply = true - case "db_getString": - args := new(DbArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - if err := args.requirements(); err != nil { - return err - } - - res, _ := api.xeth().DbGet([]byte(args.Database + args.Key)) - *reply = string(res) - case "db_putHex": - args := new(DbHexArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - if err := args.requirements(); err != nil { - return err - } - - api.xeth().DbPut([]byte(args.Database+args.Key), args.Value) - *reply = true - case "db_getHex": - args := new(DbHexArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - if err := args.requirements(); err != nil { - return err - } - - res, _ := api.xeth().DbGet([]byte(args.Database + args.Key)) - *reply = newHexData(res) - - case "shh_version": - // Short circuit if whisper is not running - if api.xeth().Whisper() == nil { - return NewNotAvailableError(req.Method, "whisper offline") - } - // Retrieves the currently running whisper protocol version - *reply = api.xeth().WhisperVersion() - - case "shh_post": - // Short circuit if whisper is not running - if api.xeth().Whisper() == nil { - return NewNotAvailableError(req.Method, "whisper offline") - } - // Injects a new message into the whisper network - args := new(WhisperMessageArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - err := api.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl) - if err != nil { - return err - } - *reply = true - - case "shh_newIdentity": - // Short circuit if whisper is not running - if api.xeth().Whisper() == nil { - return NewNotAvailableError(req.Method, "whisper offline") - } - // Creates a new whisper identity to use for sending/receiving messages - *reply = api.xeth().Whisper().NewIdentity() - - case "shh_hasIdentity": - // Short circuit if whisper is not running - if api.xeth().Whisper() == nil { - return NewNotAvailableError(req.Method, "whisper offline") - } - // Checks if an identity if owned or not - args := new(WhisperIdentityArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = api.xeth().Whisper().HasIdentity(args.Identity) - - case "shh_newFilter": - // Short circuit if whisper is not running - if api.xeth().Whisper() == nil { - return NewNotAvailableError(req.Method, "whisper offline") - } - // Create a new filter to watch and match messages with - args := new(WhisperFilterArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - id := api.xeth().NewWhisperFilter(args.To, args.From, args.Topics) - *reply = newHexNum(big.NewInt(int64(id)).Bytes()) - - case "shh_uninstallFilter": - // Short circuit if whisper is not running - if api.xeth().Whisper() == nil { - return NewNotAvailableError(req.Method, "whisper offline") - } - // Remove an existing filter watching messages - args := new(FilterIdArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = api.xeth().UninstallWhisperFilter(args.Id) - - case "shh_getFilterChanges": - // Short circuit if whisper is not running - if api.xeth().Whisper() == nil { - return NewNotAvailableError(req.Method, "whisper offline") - } - // Retrieve all the new messages arrived since the last request - args := new(FilterIdArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = api.xeth().WhisperMessagesChanged(args.Id) - - case "shh_getMessages": - // Short circuit if whisper is not running - if api.xeth().Whisper() == nil { - return NewNotAvailableError(req.Method, "whisper offline") - } - // Retrieve all the cached messages matching a specific, existing filter - args := new(FilterIdArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - *reply = api.xeth().WhisperMessages(args.Id) - - case "eth_hashrate": - *reply = newHexNum(api.xeth().HashRate()) - case "ext_disasm": - args := new(SourceArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - - *reply = vm.Disasm(common.FromHex(args.Source)) - - // case "eth_register": - // // Placeholder for actual type - // args := new(HashIndexArgs) - // if err := json.Unmarshal(req.Params, &args); err != nil { - // return err - // } - // *reply = api.xeth().Register(args.Hash) - // case "eth_unregister": - // args := new(HashIndexArgs) - // if err := json.Unmarshal(req.Params, &args); err != nil { - // return err - // } - // *reply = api.xeth().Unregister(args.Hash) - // case "eth_watchTx": - // args := new(HashIndexArgs) - // if err := json.Unmarshal(req.Params, &args); err != nil { - // return err - // } - // *reply = api.xeth().PullWatchTx(args.Hash) - default: - return NewNotImplementedError(req.Method) - } - - // glog.V(logger.Detail).Infof("Reply: %v\n", reply) - return nil -} - -func (api *EthereumApi) doCall(params json.RawMessage) (string, string, error) { - args := new(CallArgs) - if err := json.Unmarshal(params, &args); err != nil { - return "", "", err - } - - var gas string - if args.Gas == nil { - gas = "" - } else { - gas = args.Gas.String() - } - - var gasprice string - if args.GasPrice == nil { - gasprice = "" - } else { - gasprice = args.GasPrice.String() - } - - return api.xethAtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), gas, gasprice, args.Data) -} diff --git a/rpc/api/api.go b/rpc/api/api.go index 7fc22a263..10b448594 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -18,15 +18,13 @@ const ( TxPoolApiName = "txpool" PersonalApiName = "personal" Web3ApiName = "web3" + + JsonRpcVersion = "2.0" ) var ( - DefaultHttpRpcApis = strings.Join([]string{ - DbApiName, EthApiName, NetApiName, Web3ApiName, - }, ",") - - // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = strings.Join([]string{ + // All API's + AllApis = strings.Join([]string{ AdminApiName, DbApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName, }, ",") diff --git a/rpc/api/args.go b/rpc/api/args.go new file mode 100644 index 000000000..4ef1b26e7 --- /dev/null +++ b/rpc/api/args.go @@ -0,0 +1,57 @@ +package api + +import ( + "encoding/json" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +type CompileArgs struct { + Source string +} + +func (args *CompileArgs) 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) + } + argstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("arg0", "is not a string") + } + args.Source = argstr + + return nil +} + +type FilterStringArgs struct { + Word string +} + +func (args *FilterStringArgs) 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) + } + + var argstr string + argstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("filter", "not a string") + } + switch argstr { + case "latest", "pending": + break + default: + return shared.NewValidationError("Word", "Must be `latest` or `pending`") + } + args.Word = argstr + return nil +} \ No newline at end of file diff --git a/rpc/args_test.go b/rpc/api/args_test.go similarity index 99% rename from rpc/args_test.go rename to rpc/api/args_test.go index fd20dbab8..4153d5432 100644 --- a/rpc/args_test.go +++ b/rpc/api/args_test.go @@ -1,4 +1,4 @@ -package rpc +package api import ( "bytes" @@ -6,6 +6,7 @@ import ( "fmt" "math/big" "testing" + "github.com/ethereum/go-ethereum/rpc/shared" ) func TestBlockheightInvalidString(t *testing.T) { @@ -68,7 +69,7 @@ func ExpectValidationError(err error) string { switch err.(type) { case nil: str = "Expected error but didn't get one" - case *ValidationError: + case *shared.ValidationError: break default: str = fmt.Sprintf("Expected *rpc.ValidationError but got %T with message `%s`", err, err.Error()) @@ -81,7 +82,7 @@ func ExpectInvalidTypeError(err error) string { switch err.(type) { case nil: str = "Expected error but didn't get one" - case *InvalidTypeError: + case *shared.InvalidTypeError: break default: str = fmt.Sprintf("Expected *rpc.InvalidTypeError but got %T with message `%s`", err, err.Error()) @@ -94,7 +95,7 @@ func ExpectInsufficientParamsError(err error) string { switch err.(type) { case nil: str = "Expected error but didn't get one" - case *InsufficientParamsError: + case *shared.InsufficientParamsError: break default: str = fmt.Sprintf("Expected *rpc.InsufficientParamsError but got %T with message %s", err, err.Error()) @@ -107,7 +108,7 @@ func ExpectDecodeParamError(err error) string { switch err.(type) { case nil: str = "Expected error but didn't get one" - case *DecodeParamError: + case *shared.DecodeParamError: break default: str = fmt.Sprintf("Expected *rpc.DecodeParamError but got %T with message `%s`", err, err.Error()) diff --git a/rpc/api_test.go b/rpc/api_test.go deleted file mode 100644 index c3546e98f..000000000 --- a/rpc/api_test.go +++ /dev/null @@ -1,205 +0,0 @@ -package rpc - -import ( - "encoding/json" - "strconv" - "testing" - - "github.com/ethereum/go-ethereum/common/compiler" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/xeth" -) - -func TestWeb3Sha3(t *testing.T) { - jsonstr := `{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}` - expected := "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" - - api := &EthereumApi{} - - var req RpcRequest - json.Unmarshal([]byte(jsonstr), &req) - - var response interface{} - _ = api.GetRequestReply(&req, &response) - - if response.(string) != expected { - t.Errorf("Expected %s got %s", expected, response) - } -} - -const solcVersion = "0.9.23" - -func TestCompileSolidity(t *testing.T) { - - solc, err := compiler.New("") - if solc == nil { - t.Skip("no solc found: skip") - } else if solc.Version() != solcVersion { - t.Skip("WARNING: skipping test because of solc different version (%v, test written for %v, may need to update)", solc.Version(), solcVersion) - } - 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` - - jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}` - - expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056" - expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]` - expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}` - expDeveloperDoc := `{"methods":{}}` - expCompilerVersion := solc.Version() - expLanguage := "Solidity" - expLanguageVersion := "0" - expSource := source - - api := NewEthereumApi(xeth.NewTest(ð.Ethereum{}, nil)) - - var req RpcRequest - json.Unmarshal([]byte(jsonstr), &req) - - var response interface{} - err = api.GetRequestReply(&req, &response) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - respjson, err := json.Marshal(response) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - var contracts = make(map[string]*compiler.Contract) - err = json.Unmarshal(respjson, &contracts) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - if len(contracts) != 1 { - t.Errorf("expected one contract, got %v", len(contracts)) - } - - contract := contracts["test"] - - if contract.Code != expCode { - t.Errorf("Expected \n%s got \n%s", expCode, contract.Code) - } - - if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` { - t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source)) - } - - if contract.Info.Language != expLanguage { - t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language) - } - - if contract.Info.LanguageVersion != expLanguageVersion { - t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion) - } - - if contract.Info.CompilerVersion != expCompilerVersion { - t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion) - } - - userdoc, err := json.Marshal(contract.Info.UserDoc) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - devdoc, err := json.Marshal(contract.Info.DeveloperDoc) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - abidef, err := json.Marshal(contract.Info.AbiDefinition) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - if string(abidef) != expAbiDefinition { - t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef)) - } - - if string(userdoc) != expUserDoc { - t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc)) - } - - if string(devdoc) != expDeveloperDoc { - t.Errorf("Expected %s got %s", expDeveloperDoc, string(devdoc)) - } -} - -// func TestDbStr(t *testing.T) { -// jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}` -// jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}` -// expected := "myString" - -// xeth := &xeth.XEth{} -// api := NewEthereumApi(xeth) -// var response interface{} - -// var req RpcRequest -// json.Unmarshal([]byte(jsonput), &req) -// _ = api.GetRequestReply(&req, &response) - -// json.Unmarshal([]byte(jsonget), &req) -// _ = api.GetRequestReply(&req, &response) - -// if response.(string) != expected { -// t.Errorf("Expected %s got %s", expected, response) -// } -// } - -// func TestDbHexStr(t *testing.T) { -// jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}` -// jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}` -// expected := "0xbeef" - -// xeth := &xeth.XEth{} -// api := NewEthereumApi(xeth) -// defer api.db.Close() -// var response interface{} - -// var req RpcRequest -// json.Unmarshal([]byte(jsonput), &req) -// _ = api.GetRequestReply(&req, &response) - -// json.Unmarshal([]byte(jsonget), &req) -// _ = api.GetRequestReply(&req, &response) - -// if response.(string) != expected { -// t.Errorf("Expected %s got %s", expected, response) -// } -// } - -// func TestFilterClose(t *testing.T) { -// t.Skip() -// api := &EthereumApi{ -// logs: make(map[int]*logFilter), -// messages: make(map[int]*whisperFilter), -// quit: make(chan struct{}), -// } - -// filterTickerTime = 1 -// api.logs[0] = &logFilter{} -// api.messages[0] = &whisperFilter{} -// var wg sync.WaitGroup -// wg.Add(1) -// go api.start() -// go func() { -// select { -// case <-time.After(500 * time.Millisecond): -// api.stop() -// wg.Done() -// } -// }() -// wg.Wait() -// if len(api.logs) != 0 { -// t.Error("expected logs to be empty") -// } - -// if len(api.messages) != 0 { -// t.Error("expected messages to be empty") -// } -// } diff --git a/rpc/args.go b/rpc/args.go deleted file mode 100644 index aa7d20549..000000000 --- a/rpc/args.go +++ /dev/null @@ -1,1230 +0,0 @@ -package rpc - -import ( - "encoding/json" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -const ( - defaultLogLimit = 100 - defaultLogOffset = 0 -) - -func blockHeightFromJson(msg json.RawMessage, number *int64) error { - var raw interface{} - if err := json.Unmarshal(msg, &raw); err != nil { - return NewDecodeParamError(err.Error()) - } - return blockHeight(raw, number) -} - -func blockHeight(raw interface{}, number *int64) error { - // Parse as integer - num, ok := raw.(float64) - if ok { - *number = int64(num) - return nil - } - - // Parse as string/hexstring - str, ok := raw.(string) - if !ok { - return NewInvalidTypeError("", "not a number or string") - } - - switch str { - case "earliest": - *number = 0 - case "latest": - *number = -1 - case "pending": - *number = -2 - default: - if common.HasHexPrefix(str) { - *number = common.String2Big(str).Int64() - } else { - return NewInvalidTypeError("blockNumber", "is not a valid string") - } - } - - return nil -} - -func numString(raw interface{}) (*big.Int, error) { - var number *big.Int - // Parse as integer - num, ok := raw.(float64) - if ok { - number = big.NewInt(int64(num)) - return number, nil - } - - // Parse as string/hexstring - str, ok := raw.(string) - if ok { - number = common.String2Big(str) - return number, nil - } - - return nil, NewInvalidTypeError("", "not a number or string") -} - -// func toNumber(v interface{}) (int64, error) { -// var str string -// if v != nil { -// var ok bool -// str, ok = v.(string) -// if !ok { -// return 0, errors.New("is not a string or undefined") -// } -// } else { -// str = "latest" -// } - -// switch str { -// case "latest": -// return -1, nil -// default: -// return int64(common.Big(v.(string)).Int64()), nil -// } -// } - -// func hashString(raw interface{}, hash *string) error { -// argstr, ok := raw.(string) -// if !ok { -// return NewInvalidTypeError("", "not a string") -// } -// v := common.IsHex(argstr) -// hash = &argstr - -// return nil -// } - -type GetBlockByHashArgs struct { - BlockHash string - IncludeTxs bool -} - -func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return NewInsufficientParamsError(len(obj), 2) - } - - argstr, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("blockHash", "not a string") - } - args.BlockHash = argstr - - args.IncludeTxs = obj[1].(bool) - - return nil -} - -type GetBlockByNumberArgs struct { - BlockNumber int64 - IncludeTxs bool -} - -func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return NewInsufficientParamsError(len(obj), 2) - } - - if err := blockHeight(obj[0], &args.BlockNumber); err != nil { - return err - } - - args.IncludeTxs = obj[1].(bool) - - return nil -} - -type NewDataArgs struct { - Data string -} - -func (args *NewDataArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - // Check for sufficient params - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - data, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("data", "not a string") - } - args.Data = data - - if len(args.Data) == 0 { - return NewValidationError("data", "is required") - } - - return nil -} - -type NewTxArgs struct { - From string - To string - Nonce *big.Int - Value *big.Int - Gas *big.Int - GasPrice *big.Int - Data string - - BlockNumber int64 -} - -type NewSigArgs struct { - From string - Data string -} - -func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - // Check for sufficient params - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - from, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("from", "not a string") - } - args.From = from - - if len(args.From) == 0 { - return NewValidationError("from", "is required") - } - - data, ok := obj[1].(string) - if !ok { - return NewInvalidTypeError("data", "not a string") - } - args.Data = data - - if len(args.Data) == 0 { - return NewValidationError("data", "is required") - } - - return nil -} - -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{} - Data string - } - - // Decode byte slice to array of RawMessages - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - // Check for sufficient params - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - // Decode 0th RawMessage to temporary struct - if err := json.Unmarshal(obj[0], &ext); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(ext.From) == 0 { - return NewValidationError("from", "is required") - } - - args.From = ext.From - args.To = ext.To - 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 { - num, err = numString(ext.Value) - if err != nil { - return err - } - } - args.Value = num - - num = nil - if ext.Gas != nil { - if num, err = numString(ext.Gas); err != nil { - return err - } - } else { - num = nil - } - args.Gas = num - - num = nil - if ext.GasPrice != nil { - if num, err = numString(ext.GasPrice); err != nil { - return err - } - } else { - num = nil - } - args.GasPrice = num - - // 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 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 NewDecodeParamError(err.Error()) - } - - // Check for sufficient params - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - // Decode 0th RawMessage to temporary struct - if err := json.Unmarshal(obj[0], &ext); err != nil { - return NewDecodeParamError(err.Error()) - } - - args.From = ext.From - - if len(ext.To) == 0 { - return 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 { - if num, err = numString(ext.Gas); err != nil { - return err - } - } else { - num = nil - } - args.Gas = num - - if ext.GasPrice != nil { - if num, err = numString(ext.GasPrice); err != nil { - return err - } - } else { - num = nil - } - 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 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 NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - addstr, ok := obj[0].(string) - if !ok { - return 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 - Key string - BlockNumber int64 -} - -func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return NewInsufficientParamsError(len(obj), 2) - } - - addstr, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("address", "not a string") - } - args.Address = addstr - - keystr, ok := obj[1].(string) - if !ok { - return 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 NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - addstr, ok := obj[0].(string) - if !ok { - return 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 GetBalanceArgs struct { - Address string - BlockNumber int64 -} - -func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - addstr, ok := obj[0].(string) - if !ok { - return 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 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 NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - addstr, ok := obj[0].(string) - if !ok { - return 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 BlockNumArg struct { - BlockNumber int64 -} - -func (args *BlockNumArg) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - if err := blockHeight(obj[0], &args.BlockNumber); err != nil { - return err - } - - 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 NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return 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 HashArgs struct { - Hash string -} - -func (args *HashArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - arg0, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("hash", "not a string") - } - args.Hash = arg0 - - 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 NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return NewInsufficientParamsError(len(obj), 2) - } - - arg0, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("hash", "not a string") - } - args.Hash = arg0 - - arg1, ok := obj[1].(string) - if !ok { - return NewInvalidTypeError("index", "not a string") - } - args.Index = common.Big(arg1).Int64() - - return nil -} - -type Sha3Args struct { - Data string -} - -func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - argstr, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("data", "is not a string") - } - args.Data = argstr - return nil -} - -type BlockFilterArgs struct { - Earliest int64 - Latest int64 - Address []string - Topics [][]string - Skip int - Max int -} - -func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { - var obj []struct { - FromBlock interface{} `json:"fromBlock"` - ToBlock interface{} `json:"toBlock"` - Limit interface{} `json:"limit"` - Offset interface{} `json:"offset"` - Address interface{} `json:"address"` - Topics interface{} `json:"topics"` - } - - if err = json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - // args.Earliest, err = toNumber(obj[0].ToBlock) - // if err != nil { - // return NewDecodeParamError(fmt.Sprintf("FromBlock %v", err)) - // } - // args.Latest, err = toNumber(obj[0].FromBlock) - // if err != nil { - // return NewDecodeParamError(fmt.Sprintf("ToBlock %v", err)) - - var num int64 - var numBig *big.Int - - // if blank then latest - if obj[0].FromBlock == nil { - num = -1 - } else { - if err := blockHeight(obj[0].FromBlock, &num); err != nil { - return err - } - } - // if -2 or other "silly" number, use latest - if num < 0 { - args.Earliest = -1 //latest block - } else { - args.Earliest = num - } - - // if blank than latest - if obj[0].ToBlock == nil { - num = -1 - } else { - if err := blockHeight(obj[0].ToBlock, &num); err != nil { - return err - } - } - args.Latest = num - - if obj[0].Limit == nil { - numBig = big.NewInt(defaultLogLimit) - } else { - if numBig, err = numString(obj[0].Limit); err != nil { - return err - } - } - args.Max = int(numBig.Int64()) - - if obj[0].Offset == nil { - numBig = big.NewInt(defaultLogOffset) - } else { - if numBig, err = numString(obj[0].Offset); err != nil { - return err - } - } - args.Skip = int(numBig.Int64()) - - if obj[0].Address != nil { - marg, ok := obj[0].Address.([]interface{}) - if ok { - v := make([]string, len(marg)) - for i, arg := range marg { - argstr, ok := arg.(string) - if !ok { - return NewInvalidTypeError(fmt.Sprintf("address[%d]", i), "is not a string") - } - v[i] = argstr - } - args.Address = v - } else { - argstr, ok := obj[0].Address.(string) - if ok { - v := make([]string, 1) - v[0] = argstr - args.Address = v - } else { - return NewInvalidTypeError("address", "is not a string or array") - } - } - } - - if obj[0].Topics != nil { - other, ok := obj[0].Topics.([]interface{}) - if ok { - topicdbl := make([][]string, len(other)) - for i, iv := range other { - if argstr, ok := iv.(string); ok { - // Found a string, push into first element of array - topicsgl := make([]string, 1) - topicsgl[0] = argstr - topicdbl[i] = topicsgl - } else if argarray, ok := iv.([]interface{}); ok { - // Found an array of other - topicdbl[i] = make([]string, len(argarray)) - for j, jv := range argarray { - if v, ok := jv.(string); ok { - topicdbl[i][j] = v - } else if jv == nil { - topicdbl[i][j] = "" - } else { - return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string") - } - } - } else if iv == nil { - topicdbl[i] = []string{""} - } else { - return NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array") - } - } - args.Topics = topicdbl - return nil - } else { - return NewInvalidTypeError("topic", "is not a string or array") - } - } - - return nil -} - -type DbArgs struct { - Database string - Key string - Value []byte -} - -func (args *DbArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return NewInsufficientParamsError(len(obj), 2) - } - - var objstr string - var ok bool - - if objstr, ok = obj[0].(string); !ok { - return NewInvalidTypeError("database", "not a string") - } - args.Database = objstr - - if objstr, ok = obj[1].(string); !ok { - return NewInvalidTypeError("key", "not a string") - } - args.Key = objstr - - if len(obj) > 2 { - objstr, ok = obj[2].(string) - if !ok { - return NewInvalidTypeError("value", "not a string") - } - - args.Value = []byte(objstr) - } - - return nil -} - -func (a *DbArgs) requirements() error { - if len(a.Database) == 0 { - return NewValidationError("Database", "cannot be blank") - } - if len(a.Key) == 0 { - return NewValidationError("Key", "cannot be blank") - } - return nil -} - -type DbHexArgs struct { - Database string - Key string - Value []byte -} - -func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return NewInsufficientParamsError(len(obj), 2) - } - - var objstr string - var ok bool - - if objstr, ok = obj[0].(string); !ok { - return NewInvalidTypeError("database", "not a string") - } - args.Database = objstr - - if objstr, ok = obj[1].(string); !ok { - return NewInvalidTypeError("key", "not a string") - } - args.Key = objstr - - if len(obj) > 2 { - objstr, ok = obj[2].(string) - if !ok { - return NewInvalidTypeError("value", "not a string") - } - - args.Value = common.FromHex(objstr) - } - - return nil -} - -func (a *DbHexArgs) requirements() error { - if len(a.Database) == 0 { - return NewValidationError("Database", "cannot be blank") - } - if len(a.Key) == 0 { - return NewValidationError("Key", "cannot be blank") - } - return nil -} - -type WhisperMessageArgs struct { - Payload string - To string - From string - Topics []string - Priority uint32 - Ttl uint32 -} - -func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { - var obj []struct { - Payload string - To string - From string - Topics []string - Priority interface{} - Ttl interface{} - } - - if err = json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - args.Payload = obj[0].Payload - args.To = obj[0].To - args.From = obj[0].From - args.Topics = obj[0].Topics - - var num *big.Int - if num, err = numString(obj[0].Priority); err != nil { - return err - } - args.Priority = uint32(num.Int64()) - - if num, err = numString(obj[0].Ttl); err != nil { - return err - } - args.Ttl = uint32(num.Int64()) - - return nil -} - -type CompileArgs struct { - Source string -} - -func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - argstr, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("arg0", "is not a string") - } - args.Source = argstr - - return nil -} - -type FilterStringArgs struct { - Word string -} - -func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - var argstr string - argstr, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("filter", "not a string") - } - switch argstr { - case "latest", "pending": - break - default: - return NewValidationError("Word", "Must be `latest` or `pending`") - } - args.Word = argstr - return nil -} - -type FilterIdArgs struct { - Id int -} - -func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - var num *big.Int - if num, err = numString(obj[0]); err != nil { - return err - } - args.Id = int(num.Int64()) - - return nil -} - -type WhisperIdentityArgs struct { - Identity string -} - -func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - argstr, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("arg0", "not a string") - } - // if !common.IsHex(argstr) { - // return NewValidationError("arg0", "not a hexstring") - // } - args.Identity = argstr - - return nil -} - -type WhisperFilterArgs struct { - To string - From string - Topics [][]string -} - -// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a -// JSON message blob into a WhisperFilterArgs structure. -func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { - // Unmarshal the JSON message and sanity check - var obj []struct { - To interface{} `json:"to"` - From interface{} `json:"from"` - Topics interface{} `json:"topics"` - } - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - // Retrieve the simple data contents of the filter arguments - if obj[0].To == nil { - args.To = "" - } else { - argstr, ok := obj[0].To.(string) - if !ok { - return NewInvalidTypeError("to", "is not a string") - } - args.To = argstr - } - if obj[0].From == nil { - args.From = "" - } else { - argstr, ok := obj[0].From.(string) - if !ok { - return NewInvalidTypeError("from", "is not a string") - } - args.From = argstr - } - // Construct the nested topic array - if obj[0].Topics != nil { - // Make sure we have an actual topic array - list, ok := obj[0].Topics.([]interface{}) - if !ok { - return NewInvalidTypeError("topics", "is not an array") - } - // Iterate over each topic and handle nil, string or array - topics := make([][]string, len(list)) - for idx, field := range list { - switch value := field.(type) { - case nil: - topics[idx] = []string{} - - case string: - topics[idx] = []string{value} - - case []interface{}: - topics[idx] = make([]string, len(value)) - for i, nested := range value { - switch value := nested.(type) { - case nil: - topics[idx][i] = "" - - case string: - topics[idx][i] = value - - default: - return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string") - } - } - default: - return NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array") - } - } - args.Topics = topics - } - return nil -} - -type SubmitWorkArgs struct { - Nonce uint64 - Header string - Digest string -} - -func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err = json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - - if len(obj) < 3 { - return NewInsufficientParamsError(len(obj), 3) - } - - var objstr string - var ok bool - if objstr, ok = obj[0].(string); !ok { - return NewInvalidTypeError("nonce", "not a string") - } - - args.Nonce = common.String2Big(objstr).Uint64() - if objstr, ok = obj[1].(string); !ok { - return NewInvalidTypeError("header", "not a string") - } - - args.Header = objstr - - if objstr, ok = obj[2].(string); !ok { - return NewInvalidTypeError("digest", "not a string") - } - - args.Digest = objstr - - 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 NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } - - arg0, ok := obj[0].(string) - if !ok { - return NewInvalidTypeError("source code", "not a string") - } - args.Source = arg0 - - return nil -} diff --git a/rpc/comms/comms.go b/rpc/comms/comms.go index 2af63e55d..050e7b4e2 100644 --- a/rpc/comms/comms.go +++ b/rpc/comms/comms.go @@ -9,16 +9,32 @@ import ( "github.com/ethereum/go-ethereum/rpc/api" "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/shared" + "strings" ) const ( - jsonrpcver = "2.0" maxHttpSizeReqLength = 1024 * 1024 // 1MB ) +var ( + // List with all API's which are offered over the in proc interface by default + DefaultInProcApis = api.AllApis + + // List with all API's which are offered over the IPC interface by default + DefaultIpcApis = api.AllApis + + // List with API's which are offered over thr HTTP/RPC interface by default + DefaultHttpRpcApis = strings.Join([]string{ + api.DbApiName, api.EthApiName, api.NetApiName, api.Web3ApiName, + }, ",") +) + type EthereumClient interface { + // Close underlaying connection Close() + // Send request Send(interface{}) error + // Receive response Recv() (interface{}, error) } diff --git a/rpc/comms/http.go b/rpc/comms/http.go index 1fea8dc1d..c0ea2cc78 100644 --- a/rpc/comms/http.go +++ b/rpc/comms/http.go @@ -63,3 +63,27 @@ func StopHttp() { httpListener = nil } } + + +type httpClient struct { + codec codec.ApiCoder +} + +// Create a new in process client +func NewHttpClient(cfg HttpConfig, codec codec.Codec) *httpClient { + return &httpClient{ + codec: codec.New(nil), + } +} + +func (self *httpClient) Close() { + // do nothing +} + +func (self *httpClient) Send(req interface{}) error { + return nil +} + +func (self *httpClient) Recv() (interface{}, error) { + return nil, nil +} \ No newline at end of file diff --git a/rpc/comms/http_net.go b/rpc/comms/http_net.go index 8d1bacc06..f326f1a7e 100644 --- a/rpc/comms/http_net.go +++ b/rpc/comms/http_net.go @@ -90,7 +90,7 @@ func newStoppableHandler(h http.Handler, stop chan struct{}) http.Handler { case <-stop: w.Header().Set("Content-Type", "application/json") err := fmt.Errorf("RPC service stopped") - response := shared.NewRpcResponse(-1, jsonrpcver, nil, err) + response := shared.NewRpcResponse(-1, api.JsonRpcVersion, nil, err) httpSend(w, response) default: h.ServeHTTP(w, r) @@ -110,14 +110,14 @@ func httpSend(writer io.Writer, v interface{}) (n int, err error) { return writer.Write(payload) } -func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler { +func gethHttpHandler(codec codec.Codec, a api.EthereumApi) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") // Limit request size to resist DoS if req.ContentLength > maxHttpSizeReqLength { err := fmt.Errorf("Request too large") - response := shared.NewRpcErrorResponse(-1, jsonrpcver, -32700, err) + response := shared.NewRpcErrorResponse(-1, api.JsonRpcVersion, -32700, err) httpSend(w, &response) return } @@ -126,7 +126,7 @@ func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler { payload, err := ioutil.ReadAll(req.Body) if err != nil { err := fmt.Errorf("Could not read request body") - response := shared.NewRpcErrorResponse(-1, jsonrpcver, -32700, err) + response := shared.NewRpcErrorResponse(-1, api.JsonRpcVersion, -32700, err) httpSend(w, &response) return } @@ -134,7 +134,7 @@ func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler { c := codec.New(nil) var rpcReq shared.Request if err = c.Decode(payload, &rpcReq); err == nil { - reply, err := api.Execute(&rpcReq) + reply, err := a.Execute(&rpcReq) res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) httpSend(w, &res) return @@ -146,7 +146,7 @@ func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler { resCount := 0 for i, rpcReq := range reqBatch { - reply, err := api.Execute(&rpcReq) + reply, err := a.Execute(&rpcReq) if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) resCount += 1 @@ -161,7 +161,7 @@ func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler { // invalid request err = fmt.Errorf("Could not decode request") - res := shared.NewRpcErrorResponse(-1, jsonrpcver, -32600, err) + res := shared.NewRpcErrorResponse(-1, api.JsonRpcVersion, -32600, err) httpSend(w, res) }) } diff --git a/rpc/comms/inproc.go b/rpc/comms/inproc.go new file mode 100644 index 000000000..89cb93cdc --- /dev/null +++ b/rpc/comms/inproc.go @@ -0,0 +1,53 @@ +package comms + +import ( + "github.com/ethereum/go-ethereum/rpc/api" + "github.com/ethereum/go-ethereum/rpc/shared" + "fmt" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/xeth" + "github.com/ethereum/go-ethereum/eth" +) + +type InProcClient struct { + api api.EthereumApi + codec codec.Codec + lastId interface{} + lastJsonrpc string + lastErr error + lastRes interface{} +} + +// Create a new in process client +func NewInProcClient(codec codec.Codec) *InProcClient { + return &InProcClient{ + codec: codec, + } +} + +func (self *InProcClient) Close() { + // do nothing +} + +// Need to setup api support +func (self *InProcClient) Initialize(xeth *xeth.XEth, eth *eth.Ethereum) { + if apis, err := api.ParseApiString(api.AllApis, self.codec, xeth, eth); err == nil { + self.api = api.Merge(apis...) + } +} + +func (self *InProcClient) Send(req interface{}) error { + if r, ok := req.(*shared.Request); ok { + self.lastId = r.Id + self.lastJsonrpc = r.Jsonrpc + self.lastRes, self.lastErr = self.api.Execute(r) + return self.lastErr + } + + return fmt.Errorf("Invalid request (%T)", req) +} + +func (self *InProcClient) Recv() (interface{}, error) { + return self.lastRes, self.lastErr + //return *shared.NewRpcResponse(self.lastId, self.lastJsonrpc, self.lastRes, self.lastErr), nil +} diff --git a/rpc/comms/ipc.go b/rpc/comms/ipc.go index a75039d17..a07203803 100644 --- a/rpc/comms/ipc.go +++ b/rpc/comms/ipc.go @@ -10,19 +10,19 @@ type IpcConfig struct { } type ipcClient struct { - c codec.ApiCoder + codec codec.ApiCoder } func (self *ipcClient) Close() { - self.c.Close() + self.codec.Close() } func (self *ipcClient) Send(req interface{}) error { - return self.c.WriteResponse(req) + return self.codec.WriteResponse(req) } func (self *ipcClient) Recv() (interface{}, error) { - return self.c.ReadResponse() + return self.codec.ReadResponse() } // Create a new IPC client, UNIX domain socket on posix, named pipe on Windows diff --git a/rpc/http.go b/rpc/http.go deleted file mode 100644 index f37e102f5..000000000 --- a/rpc/http.go +++ /dev/null @@ -1,163 +0,0 @@ -package rpc - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "strings" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/xeth" - "github.com/rs/cors" -) - -var rpclistener *stoppableTCPListener - -const ( - jsonrpcver = "2.0" - maxSizeReqLength = 1024 * 1024 // 1MB -) - -func Start(pipe *xeth.XEth, config RpcConfig) error { - if rpclistener != nil { - if fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort) != rpclistener.Addr().String() { - return fmt.Errorf("RPC service already running on %s ", rpclistener.Addr().String()) - } - return nil // RPC service already running on given host/port - } - - l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort)) - if err != nil { - glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err) - return err - } - rpclistener = l - - var handler http.Handler - if len(config.CorsDomain) > 0 { - var opts cors.Options - opts.AllowedMethods = []string{"POST"} - opts.AllowedOrigins = strings.Split(config.CorsDomain, " ") - - c := cors.New(opts) - handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop) - } else { - handler = newStoppableHandler(JSONRPC(pipe), l.stop) - } - - go http.Serve(l, handler) - - return nil -} - -func Stop() error { - if rpclistener != nil { - rpclistener.Stop() - rpclistener = nil - } - - return nil -} - -// JSONRPC returns a handler that implements the Ethereum JSON-RPC API. -func JSONRPC(pipe *xeth.XEth) http.Handler { - api := NewEthereumApi(pipe) - - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Content-Type", "application/json") - - // Limit request size to resist DoS - if req.ContentLength > maxSizeReqLength { - jsonerr := &RpcErrorObject{-32700, "Request too large"} - send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) - return - } - - // Read request body - defer req.Body.Close() - body, err := ioutil.ReadAll(req.Body) - if err != nil { - jsonerr := &RpcErrorObject{-32700, "Could not read request body"} - send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) - } - - // Try to parse the request as a single - var reqSingle RpcRequest - if err := json.Unmarshal(body, &reqSingle); err == nil { - response := RpcResponse(api, &reqSingle) - if reqSingle.Id != nil { - send(w, &response) - } - return - } - - // Try to parse the request to batch - var reqBatch []RpcRequest - if err := json.Unmarshal(body, &reqBatch); err == nil { - // Build response batch - resBatch := make([]*interface{}, len(reqBatch)) - resCount := 0 - - for i, request := range reqBatch { - response := RpcResponse(api, &request) - // this leaves nil entries in the response batch for later removal - if request.Id != nil { - resBatch[i] = response - resCount = resCount + 1 - } - } - - // make response omitting nil entries - respBatchComp := make([]*interface{}, resCount) - for _, v := range resBatch { - if v != nil { - respBatchComp[len(respBatchComp)-resCount] = v - resCount = resCount - 1 - } - } - - send(w, respBatchComp) - return - } - - // Not a batch or single request, error - jsonerr := &RpcErrorObject{-32600, "Could not decode request"} - send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) - }) -} - -func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} { - var reply, response interface{} - reserr := api.GetRequestReply(request, &reply) - switch reserr.(type) { - case nil: - response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply} - case *NotImplementedError, *NotAvailableError: - jsonerr := &RpcErrorObject{-32601, reserr.Error()} - response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} - case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: - jsonerr := &RpcErrorObject{-32602, reserr.Error()} - response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} - default: - jsonerr := &RpcErrorObject{-32603, reserr.Error()} - response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} - } - - glog.V(logger.Detail).Infof("Generated response: %T %s", response, response) - return &response -} - -func send(writer io.Writer, v interface{}) (n int, err error) { - var payload []byte - payload, err = json.MarshalIndent(v, "", "\t") - if err != nil { - glog.V(logger.Error).Infoln("Error marshalling JSON", err) - return 0, err - } - glog.V(logger.Detail).Infof("Sending payload: %s", payload) - - return writer.Write(payload) -} diff --git a/rpc/jeth.go b/rpc/jeth.go index e578775bb..08d6a9878 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -1,31 +1,28 @@ package rpc import ( - "encoding/json" - "fmt" - - "reflect" - "github.com/ethereum/go-ethereum/jsre" - "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/api" "github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/shared" "github.com/robertkrimen/otto" + "encoding/json" + "fmt" ) type Jeth struct { - ethApi *EthereumApi - re *jsre.JSRE - ipcpath string + ethApi api.EthereumApi + re *jsre.JSRE + client comms.EthereumClient } -func NewJeth(ethApi *EthereumApi, re *jsre.JSRE, ipcpath string) *Jeth { - return &Jeth{ethApi, re, ipcpath} +func NewJeth(ethApi api.EthereumApi, re *jsre.JSRE, client comms.EthereumClient) *Jeth { + return &Jeth{ethApi, re, client} } func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) { - rpcerr := &RpcErrorObject{code, msg} - call.Otto.Set("ret_jsonrpc", jsonrpcver) + rpcerr := &shared.ErrorObject{code, msg} + call.Otto.Set("ret_jsonrpc", api.JsonRpcVersion) call.Otto.Set("ret_id", id) call.Otto.Set("ret_error", rpcerr) response, _ = call.Otto.Run(` @@ -34,6 +31,7 @@ func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface return } + func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { reqif, err := call.Argument(0).Export() if err != nil { @@ -41,11 +39,11 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { } jsonreq, err := json.Marshal(reqif) - var reqs []RpcRequest + var reqs []shared.Request batch := true err = json.Unmarshal(jsonreq, &reqs) if err != nil { - reqs = make([]RpcRequest, 1) + reqs = make([]shared.Request, 1) err = json.Unmarshal(jsonreq, &reqs[0]) batch = false } @@ -55,12 +53,18 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { for i, req := range reqs { var respif interface{} - err = self.ethApi.GetRequestReply(&req, &respif) + err := self.client.Send(&req)//self.ethApi.Execute(&req) + if err != nil { + fmt.Println("Error request:", err) + return self.err(call, -32603, err.Error(), req.Id) + } + respif, err = self.client.Recv() if err != nil { fmt.Println("Error response:", err) return self.err(call, -32603, err.Error(), req.Id) } - call.Otto.Set("ret_jsonrpc", jsonrpcver) + + call.Otto.Set("ret_jsonrpc", api.JsonRpcVersion) call.Otto.Set("ret_id", req.Id) res, _ := json.Marshal(respif) @@ -88,6 +92,7 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { return } +/* func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) { reqif, err := call.Argument(0).Export() if err != nil { @@ -102,11 +107,11 @@ func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) { defer client.Close() jsonreq, err := json.Marshal(reqif) - var reqs []RpcRequest + var reqs []shared.Request batch := true err = json.Unmarshal(jsonreq, &reqs) if err != nil { - reqs = make([]RpcRequest, 1) + reqs = make([]shared.Request, 1) err = json.Unmarshal(jsonreq, &reqs[0]) batch = false } @@ -169,3 +174,4 @@ func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) { return } +*/ diff --git a/rpc/responses.go b/rpc/responses.go deleted file mode 100644 index 9fdf60c02..000000000 --- a/rpc/responses.go +++ /dev/null @@ -1,316 +0,0 @@ -package rpc - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" -) - -type BlockRes struct { - fullTx bool - - BlockNumber *hexnum `json:"number"` - BlockHash *hexdata `json:"hash"` - ParentHash *hexdata `json:"parentHash"` - Nonce *hexdata `json:"nonce"` - Sha3Uncles *hexdata `json:"sha3Uncles"` - LogsBloom *hexdata `json:"logsBloom"` - TransactionRoot *hexdata `json:"transactionsRoot"` - StateRoot *hexdata `json:"stateRoot"` - Miner *hexdata `json:"miner"` - Difficulty *hexnum `json:"difficulty"` - TotalDifficulty *hexnum `json:"totalDifficulty"` - Size *hexnum `json:"size"` - ExtraData *hexdata `json:"extraData"` - GasLimit *hexnum `json:"gasLimit"` - GasUsed *hexnum `json:"gasUsed"` - UnixTimestamp *hexnum `json:"timestamp"` - Transactions []*TransactionRes `json:"transactions"` - Uncles []*UncleRes `json:"uncles"` -} - -func (b *BlockRes) MarshalJSON() ([]byte, error) { - if b.fullTx { - var ext struct { - BlockNumber *hexnum `json:"number"` - BlockHash *hexdata `json:"hash"` - ParentHash *hexdata `json:"parentHash"` - Nonce *hexdata `json:"nonce"` - Sha3Uncles *hexdata `json:"sha3Uncles"` - LogsBloom *hexdata `json:"logsBloom"` - TransactionRoot *hexdata `json:"transactionsRoot"` - StateRoot *hexdata `json:"stateRoot"` - Miner *hexdata `json:"miner"` - Difficulty *hexnum `json:"difficulty"` - TotalDifficulty *hexnum `json:"totalDifficulty"` - Size *hexnum `json:"size"` - ExtraData *hexdata `json:"extraData"` - GasLimit *hexnum `json:"gasLimit"` - GasUsed *hexnum `json:"gasUsed"` - UnixTimestamp *hexnum `json:"timestamp"` - Transactions []*TransactionRes `json:"transactions"` - Uncles []*hexdata `json:"uncles"` - } - - ext.BlockNumber = b.BlockNumber - ext.BlockHash = b.BlockHash - ext.ParentHash = b.ParentHash - ext.Nonce = b.Nonce - ext.Sha3Uncles = b.Sha3Uncles - ext.LogsBloom = b.LogsBloom - ext.TransactionRoot = b.TransactionRoot - ext.StateRoot = b.StateRoot - ext.Miner = b.Miner - ext.Difficulty = b.Difficulty - ext.TotalDifficulty = b.TotalDifficulty - ext.Size = b.Size - ext.ExtraData = b.ExtraData - ext.GasLimit = b.GasLimit - ext.GasUsed = b.GasUsed - ext.UnixTimestamp = b.UnixTimestamp - ext.Transactions = b.Transactions - ext.Uncles = make([]*hexdata, len(b.Uncles)) - for i, u := range b.Uncles { - ext.Uncles[i] = u.BlockHash - } - return json.Marshal(ext) - } else { - var ext struct { - BlockNumber *hexnum `json:"number"` - BlockHash *hexdata `json:"hash"` - ParentHash *hexdata `json:"parentHash"` - Nonce *hexdata `json:"nonce"` - Sha3Uncles *hexdata `json:"sha3Uncles"` - LogsBloom *hexdata `json:"logsBloom"` - TransactionRoot *hexdata `json:"transactionsRoot"` - StateRoot *hexdata `json:"stateRoot"` - Miner *hexdata `json:"miner"` - Difficulty *hexnum `json:"difficulty"` - TotalDifficulty *hexnum `json:"totalDifficulty"` - Size *hexnum `json:"size"` - ExtraData *hexdata `json:"extraData"` - GasLimit *hexnum `json:"gasLimit"` - GasUsed *hexnum `json:"gasUsed"` - UnixTimestamp *hexnum `json:"timestamp"` - Transactions []*hexdata `json:"transactions"` - Uncles []*hexdata `json:"uncles"` - } - - ext.BlockNumber = b.BlockNumber - ext.BlockHash = b.BlockHash - ext.ParentHash = b.ParentHash - ext.Nonce = b.Nonce - ext.Sha3Uncles = b.Sha3Uncles - ext.LogsBloom = b.LogsBloom - ext.TransactionRoot = b.TransactionRoot - ext.StateRoot = b.StateRoot - ext.Miner = b.Miner - ext.Difficulty = b.Difficulty - ext.TotalDifficulty = b.TotalDifficulty - ext.Size = b.Size - ext.ExtraData = b.ExtraData - ext.GasLimit = b.GasLimit - ext.GasUsed = b.GasUsed - ext.UnixTimestamp = b.UnixTimestamp - ext.Transactions = make([]*hexdata, len(b.Transactions)) - for i, tx := range b.Transactions { - ext.Transactions[i] = tx.Hash - } - ext.Uncles = make([]*hexdata, len(b.Uncles)) - for i, u := range b.Uncles { - ext.Uncles[i] = u.BlockHash - } - return json.Marshal(ext) - } -} - -func NewBlockRes(block *types.Block, fullTx bool) *BlockRes { - if block == nil { - return nil - } - - res := new(BlockRes) - res.fullTx = fullTx - res.BlockNumber = newHexNum(block.Number()) - res.BlockHash = newHexData(block.Hash()) - res.ParentHash = newHexData(block.ParentHash()) - res.Nonce = newHexData(block.Nonce()) - res.Sha3Uncles = newHexData(block.Header().UncleHash) - res.LogsBloom = newHexData(block.Bloom()) - res.TransactionRoot = newHexData(block.Header().TxHash) - res.StateRoot = newHexData(block.Root()) - res.Miner = newHexData(block.Header().Coinbase) - res.Difficulty = newHexNum(block.Difficulty()) - res.TotalDifficulty = newHexNum(block.Td) - res.Size = newHexNum(block.Size().Int64()) - res.ExtraData = newHexData(block.Header().Extra) - res.GasLimit = newHexNum(block.GasLimit()) - res.GasUsed = newHexNum(block.GasUsed()) - res.UnixTimestamp = newHexNum(block.Time()) - - res.Transactions = make([]*TransactionRes, len(block.Transactions())) - for i, tx := range block.Transactions() { - res.Transactions[i] = NewTransactionRes(tx) - res.Transactions[i].BlockHash = res.BlockHash - res.Transactions[i].BlockNumber = res.BlockNumber - res.Transactions[i].TxIndex = newHexNum(i) - } - - res.Uncles = make([]*UncleRes, len(block.Uncles())) - for i, uncle := range block.Uncles() { - res.Uncles[i] = NewUncleRes(uncle) - } - - return res -} - -type TransactionRes struct { - Hash *hexdata `json:"hash"` - Nonce *hexnum `json:"nonce"` - BlockHash *hexdata `json:"blockHash"` - BlockNumber *hexnum `json:"blockNumber"` - TxIndex *hexnum `json:"transactionIndex"` - From *hexdata `json:"from"` - To *hexdata `json:"to"` - Value *hexnum `json:"value"` - Gas *hexnum `json:"gas"` - GasPrice *hexnum `json:"gasPrice"` - Input *hexdata `json:"input"` -} - -func NewTransactionRes(tx *types.Transaction) *TransactionRes { - if tx == nil { - return nil - } - - var v = new(TransactionRes) - v.Hash = newHexData(tx.Hash()) - v.Nonce = newHexNum(tx.Nonce()) - // v.BlockHash = - // v.BlockNumber = - // v.TxIndex = - from, _ := tx.From() - v.From = newHexData(from) - v.To = newHexData(tx.To()) - v.Value = newHexNum(tx.Value()) - v.Gas = newHexNum(tx.Gas()) - v.GasPrice = newHexNum(tx.GasPrice()) - v.Input = newHexData(tx.Data()) - return v -} - -type UncleRes struct { - BlockNumber *hexnum `json:"number"` - BlockHash *hexdata `json:"hash"` - ParentHash *hexdata `json:"parentHash"` - Nonce *hexdata `json:"nonce"` - Sha3Uncles *hexdata `json:"sha3Uncles"` - ReceiptHash *hexdata `json:"receiptHash"` - LogsBloom *hexdata `json:"logsBloom"` - TransactionRoot *hexdata `json:"transactionsRoot"` - StateRoot *hexdata `json:"stateRoot"` - Miner *hexdata `json:"miner"` - Difficulty *hexnum `json:"difficulty"` - ExtraData *hexdata `json:"extraData"` - GasLimit *hexnum `json:"gasLimit"` - GasUsed *hexnum `json:"gasUsed"` - UnixTimestamp *hexnum `json:"timestamp"` -} - -func NewUncleRes(h *types.Header) *UncleRes { - if h == nil { - return nil - } - - var v = new(UncleRes) - v.BlockNumber = newHexNum(h.Number) - v.BlockHash = newHexData(h.Hash()) - v.ParentHash = newHexData(h.ParentHash) - v.Sha3Uncles = newHexData(h.UncleHash) - v.Nonce = newHexData(h.Nonce[:]) - v.LogsBloom = newHexData(h.Bloom) - v.TransactionRoot = newHexData(h.TxHash) - v.StateRoot = newHexData(h.Root) - v.Miner = newHexData(h.Coinbase) - v.Difficulty = newHexNum(h.Difficulty) - v.ExtraData = newHexData(h.Extra) - v.GasLimit = newHexNum(h.GasLimit) - v.GasUsed = newHexNum(h.GasUsed) - v.UnixTimestamp = newHexNum(h.Time) - v.ReceiptHash = newHexData(h.ReceiptHash) - - return v -} - -// type FilterLogRes struct { -// Hash string `json:"hash"` -// Address string `json:"address"` -// Data string `json:"data"` -// BlockNumber string `json:"blockNumber"` -// TransactionHash string `json:"transactionHash"` -// BlockHash string `json:"blockHash"` -// TransactionIndex string `json:"transactionIndex"` -// LogIndex string `json:"logIndex"` -// } - -// type FilterWhisperRes struct { -// Hash string `json:"hash"` -// From string `json:"from"` -// To string `json:"to"` -// Expiry string `json:"expiry"` -// Sent string `json:"sent"` -// Ttl string `json:"ttl"` -// Topics string `json:"topics"` -// Payload string `json:"payload"` -// WorkProved string `json:"workProved"` -// } - -type LogRes struct { - Address *hexdata `json:"address"` - Topics []*hexdata `json:"topics"` - Data *hexdata `json:"data"` - BlockNumber *hexnum `json:"blockNumber"` - LogIndex *hexnum `json:"logIndex"` - BlockHash *hexdata `json:"blockHash"` - TransactionHash *hexdata `json:"transactionHash"` - TransactionIndex *hexnum `json:"transactionIndex"` -} - -func NewLogRes(log *state.Log) LogRes { - var l LogRes - l.Topics = make([]*hexdata, len(log.Topics)) - for j, topic := range log.Topics { - l.Topics[j] = newHexData(topic) - } - l.Address = newHexData(log.Address) - l.Data = newHexData(log.Data) - l.BlockNumber = newHexNum(log.Number) - l.LogIndex = newHexNum(log.Index) - l.TransactionHash = newHexData(log.TxHash) - l.TransactionIndex = newHexNum(log.TxIndex) - l.BlockHash = newHexData(log.BlockHash) - - return l -} - -func NewLogsRes(logs state.Logs) (ls []LogRes) { - ls = make([]LogRes, len(logs)) - - for i, log := range logs { - ls[i] = NewLogRes(log) - } - - return -} - -func NewHashesRes(hs []common.Hash) []string { - hashes := make([]string, len(hs)) - - for i, hash := range hs { - hashes[i] = hash.Hex() - } - - return hashes -} diff --git a/rpc/responses_test.go b/rpc/responses_test.go deleted file mode 100644 index 66323e5f5..000000000 --- a/rpc/responses_test.go +++ /dev/null @@ -1,295 +0,0 @@ -package rpc - -import ( - "encoding/json" - "fmt" - "math/big" - "regexp" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" -) - -const ( - reHash = `"0x[0-9a-f]{64}"` // 32 bytes - reHashOpt = `"(0x[0-9a-f]{64})"|null` // 32 bytes or null - reAddress = `"0x[0-9a-f]{40}"` // 20 bytes - reAddressOpt = `"0x[0-9a-f]{40}"|null` // 20 bytes or null - reNum = `"0x([1-9a-f][0-9a-f]{0,15})|0"` // must not have left-padded zeros - reNumNonZero = `"0x([1-9a-f][0-9a-f]{0,15})"` // non-zero required must not have left-padded zeros - reNumOpt = `"0x([1-9a-f][0-9a-f]{0,15})|0"|null` // must not have left-padded zeros or null - reData = `"0x[0-9a-f]*"` // can be "empty" - // reListHash = `[("\w":"0x[0-9a-f]{64}",?)*]` - // reListObj = `[("\w":(".+"|null),?)*]` -) - -func TestNewBlockRes(t *testing.T) { - tests := map[string]string{ - "number": reNum, - "hash": reHash, - "parentHash": reHash, - "nonce": reData, - "sha3Uncles": reHash, - "logsBloom": reData, - "transactionsRoot": reHash, - "stateRoot": reHash, - "miner": reAddress, - "difficulty": `"0x1"`, - "totalDifficulty": reNum, - "size": reNumNonZero, - "extraData": reData, - "gasLimit": reNum, - // "minGasPrice": "0x", - "gasUsed": reNum, - "timestamp": reNum, - // "transactions": reListHash, - // "uncles": reListHash, - } - - block := makeBlock() - v := NewBlockRes(block, false) - j, _ := json.Marshal(v) - - for k, re := range tests { - match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) - if !match { - t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j)) - } - } -} - -func TestNewBlockResTxFull(t *testing.T) { - tests := map[string]string{ - "number": reNum, - "hash": reHash, - "parentHash": reHash, - "nonce": reData, - "sha3Uncles": reHash, - "logsBloom": reData, - "transactionsRoot": reHash, - "stateRoot": reHash, - "miner": reAddress, - "difficulty": `"0x1"`, - "totalDifficulty": reNum, - "size": reNumNonZero, - "extraData": reData, - "gasLimit": reNum, - // "minGasPrice": "0x", - "gasUsed": reNum, - "timestamp": reNum, - // "transactions": reListHash, - // "uncles": reListHash, - } - - block := makeBlock() - v := NewBlockRes(block, true) - j, _ := json.Marshal(v) - - for k, re := range tests { - match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) - if !match { - t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j)) - } - } -} - -func TestBlockNil(t *testing.T) { - var block *types.Block - block = nil - u := NewBlockRes(block, false) - j, _ := json.Marshal(u) - if string(j) != "null" { - t.Errorf("Expected null but got %v", string(j)) - } -} - -func TestNewTransactionRes(t *testing.T) { - to := common.HexToAddress("0x02") - amount := big.NewInt(1) - gasAmount := big.NewInt(1) - gasPrice := big.NewInt(1) - data := []byte{1, 2, 3} - tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data) - - tests := map[string]string{ - "hash": reHash, - "nonce": reNum, - "blockHash": reHashOpt, - "blockNum": reNumOpt, - "transactionIndex": reNumOpt, - "from": reAddress, - "to": reAddressOpt, - "value": reNum, - "gas": reNum, - "gasPrice": reNum, - "input": reData, - } - - v := NewTransactionRes(tx) - v.BlockHash = newHexData(common.HexToHash("0x030201")) - v.BlockNumber = newHexNum(5) - v.TxIndex = newHexNum(0) - j, _ := json.Marshal(v) - for k, re := range tests { - match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) - if !match { - t.Error(fmt.Sprintf("`%s` output json does not match format %s. Source %s", k, re, j)) - } - } - -} - -func TestTransactionNil(t *testing.T) { - var tx *types.Transaction - tx = nil - u := NewTransactionRes(tx) - j, _ := json.Marshal(u) - if string(j) != "null" { - t.Errorf("Expected null but got %v", string(j)) - } -} - -func TestNewUncleRes(t *testing.T) { - header := makeHeader() - u := NewUncleRes(header) - tests := map[string]string{ - "number": reNum, - "hash": reHash, - "parentHash": reHash, - "nonce": reData, - "sha3Uncles": reHash, - "receiptHash": reHash, - "transactionsRoot": reHash, - "stateRoot": reHash, - "miner": reAddress, - "difficulty": reNum, - "extraData": reData, - "gasLimit": reNum, - "gasUsed": reNum, - "timestamp": reNum, - } - - j, _ := json.Marshal(u) - for k, re := range tests { - match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) - if !match { - t.Error(fmt.Sprintf("`%s` output json does not match format %s. Source %s", k, re, j)) - } - } -} - -func TestUncleNil(t *testing.T) { - var header *types.Header - header = nil - u := NewUncleRes(header) - j, _ := json.Marshal(u) - if string(j) != "null" { - t.Errorf("Expected null but got %v", string(j)) - } -} - -func TestNewLogRes(t *testing.T) { - log := makeStateLog(0) - tests := map[string]string{ - "address": reAddress, - // "topics": "[.*]" - "data": reData, - "blockNumber": reNum, - // "hash": reHash, - // "logIndex": reNum, - // "blockHash": reHash, - // "transactionHash": reHash, - "transactionIndex": reNum, - } - - v := NewLogRes(log) - j, _ := json.Marshal(v) - - for k, re := range tests { - match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) - if !match { - t.Error(fmt.Sprintf("`%s` output json does not match format %s. Got %s", k, re, j)) - } - } - -} - -func TestNewLogsRes(t *testing.T) { - logs := make([]*state.Log, 3) - logs[0] = makeStateLog(1) - logs[1] = makeStateLog(2) - logs[2] = makeStateLog(3) - tests := map[string]string{} - - v := NewLogsRes(logs) - j, _ := json.Marshal(v) - - for k, re := range tests { - match, _ := regexp.MatchString(fmt.Sprintf(`[{.*"%s":%s.*}]`, k, re), string(j)) - if !match { - t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j)) - } - } - -} - -func makeStateLog(num int) *state.Log { - address := common.HexToAddress("0x0") - data := []byte{1, 2, 3} - number := uint64(num) - topics := make([]common.Hash, 3) - topics = append(topics, common.HexToHash("0x00")) - topics = append(topics, common.HexToHash("0x10")) - topics = append(topics, common.HexToHash("0x20")) - log := state.NewLog(address, topics, data, number) - return log -} - -func makeHeader() *types.Header { - header := &types.Header{ - ParentHash: common.StringToHash("0x00"), - UncleHash: common.StringToHash("0x00"), - Coinbase: common.StringToAddress("0x00"), - Root: common.StringToHash("0x00"), - TxHash: common.StringToHash("0x00"), - ReceiptHash: common.StringToHash("0x00"), - // Bloom: - Difficulty: big.NewInt(88888888), - Number: big.NewInt(16), - GasLimit: big.NewInt(70000), - GasUsed: big.NewInt(25000), - Time: 124356789, - Extra: nil, - MixDigest: common.StringToHash("0x00"), - Nonce: [8]byte{0, 1, 2, 3, 4, 5, 6, 7}, - } - return header -} - -func makeBlock() *types.Block { - parentHash := common.HexToHash("0x01") - coinbase := common.HexToAddress("0x01") - root := common.HexToHash("0x01") - difficulty := common.Big1 - nonce := uint64(1) - block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, nil) - - txto := common.HexToAddress("0x02") - txamount := big.NewInt(1) - txgasAmount := big.NewInt(1) - txgasPrice := big.NewInt(1) - txdata := []byte{1, 2, 3} - - tx := types.NewTransactionMessage(txto, txamount, txgasAmount, txgasPrice, txdata) - txs := make([]*types.Transaction, 1) - txs[0] = tx - block.SetTransactions(txs) - - uncles := make([]*types.Header, 1) - uncles[0] = makeHeader() - block.SetUncles(uncles) - - return block -} diff --git a/rpc/types.go b/rpc/types.go deleted file mode 100644 index 1f49a3dea..000000000 --- a/rpc/types.go +++ /dev/null @@ -1,381 +0,0 @@ -/* - This file is part of go-ethereum - - go-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - go-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with go-ethereum. If not, see . -*/ -package rpc - -import ( - "encoding/binary" - "encoding/hex" - "encoding/json" - "fmt" - "math/big" - "strings" - - "errors" - "net" - "net/http" - "time" - - "io" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -type hexdata struct { - data []byte - isNil bool -} - -func (d *hexdata) String() string { - return "0x" + common.Bytes2Hex(d.data) -} - -func (d *hexdata) MarshalJSON() ([]byte, error) { - if d.isNil { - return json.Marshal(nil) - } - return json.Marshal(d.String()) -} - -func newHexData(input interface{}) *hexdata { - d := new(hexdata) - - if input == nil { - d.isNil = true - return d - } - switch input := input.(type) { - case []byte: - d.data = input - case common.Hash: - d.data = input.Bytes() - case *common.Hash: - if input == nil { - d.isNil = true - } else { - d.data = input.Bytes() - } - case common.Address: - d.data = input.Bytes() - case *common.Address: - if input == nil { - d.isNil = true - } else { - d.data = input.Bytes() - } - case types.Bloom: - d.data = input.Bytes() - case *types.Bloom: - if input == nil { - d.isNil = true - } else { - d.data = input.Bytes() - } - case *big.Int: - if input == nil { - d.isNil = true - } else { - d.data = input.Bytes() - } - case int64: - d.data = big.NewInt(input).Bytes() - case uint64: - buff := make([]byte, 8) - binary.BigEndian.PutUint64(buff, input) - d.data = buff - case int: - d.data = big.NewInt(int64(input)).Bytes() - case uint: - d.data = big.NewInt(int64(input)).Bytes() - case int8: - d.data = big.NewInt(int64(input)).Bytes() - case uint8: - d.data = big.NewInt(int64(input)).Bytes() - case int16: - d.data = big.NewInt(int64(input)).Bytes() - case uint16: - buff := make([]byte, 2) - binary.BigEndian.PutUint16(buff, input) - d.data = buff - case int32: - d.data = big.NewInt(int64(input)).Bytes() - case uint32: - buff := make([]byte, 4) - binary.BigEndian.PutUint32(buff, input) - d.data = buff - case string: // hexstring - // aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded - bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x")) - if err != nil { - d.isNil = true - } else { - d.data = bytes - } - default: - d.isNil = true - } - - return d -} - -type hexnum struct { - data []byte - isNil bool -} - -func (d *hexnum) String() string { - // Get hex string from bytes - out := common.Bytes2Hex(d.data) - // Trim leading 0s - out = strings.TrimLeft(out, "0") - // Output "0x0" when value is 0 - if len(out) == 0 { - out = "0" - } - return "0x" + out -} - -func (d *hexnum) MarshalJSON() ([]byte, error) { - if d.isNil { - return json.Marshal(nil) - } - return json.Marshal(d.String()) -} - -func newHexNum(input interface{}) *hexnum { - d := new(hexnum) - - d.data = newHexData(input).data - - return d -} - -type RpcConfig struct { - ListenAddress string - ListenPort uint - CorsDomain string -} - -type InvalidTypeError struct { - method string - msg string -} - -func (e *InvalidTypeError) Error() string { - return fmt.Sprintf("invalid type on field %s: %s", e.method, e.msg) -} - -func NewInvalidTypeError(method, msg string) *InvalidTypeError { - return &InvalidTypeError{ - method: method, - msg: msg, - } -} - -type InsufficientParamsError struct { - have int - want int -} - -func (e *InsufficientParamsError) Error() string { - return fmt.Sprintf("insufficient params, want %d have %d", e.want, e.have) -} - -func NewInsufficientParamsError(have int, want int) *InsufficientParamsError { - return &InsufficientParamsError{ - have: have, - want: want, - } -} - -type NotImplementedError struct { - Method string -} - -func (e *NotImplementedError) Error() string { - return fmt.Sprintf("%s method not implemented", e.Method) -} - -func NewNotImplementedError(method string) *NotImplementedError { - return &NotImplementedError{ - Method: method, - } -} - -type NotAvailableError struct { - Method string - Reason string -} - -func (e *NotAvailableError) Error() string { - return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason) -} - -func NewNotAvailableError(method string, reason string) *NotAvailableError { - return &NotAvailableError{ - Method: method, - Reason: reason, - } -} - -type DecodeParamError struct { - err string -} - -func (e *DecodeParamError) Error() string { - return fmt.Sprintf("could not decode, %s", e.err) - -} - -func NewDecodeParamError(errstr string) error { - return &DecodeParamError{ - err: errstr, - } -} - -type ValidationError struct { - ParamName string - msg string -} - -func (e *ValidationError) Error() string { - return fmt.Sprintf("%s not valid, %s", e.ParamName, e.msg) -} - -func NewValidationError(param string, msg string) error { - return &ValidationError{ - ParamName: param, - msg: msg, - } -} - -type RpcRequest struct { - Id interface{} `json:"id"` - Jsonrpc string `json:"jsonrpc"` - Method string `json:"method"` - Params json.RawMessage `json:"params"` -} - -type RpcSuccessResponse struct { - Id interface{} `json:"id"` - Jsonrpc string `json:"jsonrpc"` - Result interface{} `json:"result"` -} - -type RpcErrorResponse struct { - Id interface{} `json:"id"` - Jsonrpc string `json:"jsonrpc"` - Error *RpcErrorObject `json:"error"` -} - -type RpcErrorObject struct { - Code int `json:"code"` - Message string `json:"message"` - // Data interface{} `json:"data"` -} - -type listenerHasStoppedError struct { - msg string -} - -func (self listenerHasStoppedError) Error() string { - return self.msg -} - -var listenerStoppedError = listenerHasStoppedError{"Listener stopped"} - -// When https://github.com/golang/go/issues/4674 is fixed this could be replaced -type stoppableTCPListener struct { - *net.TCPListener - stop chan struct{} // closed when the listener must stop -} - -// Wraps the default handler and checks if the RPC service was stopped. In that case it returns an -// error indicating that the service was stopped. This will only happen for connections which are -// kept open (HTTP keep-alive) when the RPC service was shutdown. -func newStoppableHandler(h http.Handler, stop chan struct{}) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - select { - case <-stop: - w.Header().Set("Content-Type", "application/json") - jsonerr := &RpcErrorObject{-32603, "RPC service stopped"} - send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) - default: - h.ServeHTTP(w, r) - } - }) -} - -// Stop the listener and all accepted and still active connections. -func (self *stoppableTCPListener) Stop() { - close(self.stop) -} - -func newStoppableTCPListener(addr string) (*stoppableTCPListener, error) { - wl, err := net.Listen("tcp", addr) - if err != nil { - return nil, err - } - - if tcpl, ok := wl.(*net.TCPListener); ok { - stop := make(chan struct{}) - l := &stoppableTCPListener{tcpl, stop} - return l, nil - } - - return nil, errors.New("Unable to create TCP listener for RPC service") -} - -func (self *stoppableTCPListener) Accept() (net.Conn, error) { - for { - self.SetDeadline(time.Now().Add(time.Duration(1 * time.Second))) - c, err := self.TCPListener.AcceptTCP() - - select { - case <-self.stop: - if c != nil { // accept timeout - c.Close() - } - self.TCPListener.Close() - return nil, listenerStoppedError - default: - } - - if err != nil { - if netErr, ok := err.(net.Error); ok && netErr.Timeout() && netErr.Temporary() { - continue // regular timeout - } - } - - return &closableConnection{c, self.stop}, err - } -} - -type closableConnection struct { - *net.TCPConn - closed chan struct{} -} - -func (self *closableConnection) Read(b []byte) (n int, err error) { - select { - case <-self.closed: - self.TCPConn.Close() - return 0, io.EOF - default: - return self.TCPConn.Read(b) - } -} diff --git a/rpc/types_test.go b/rpc/types_test.go deleted file mode 100644 index 9ef7b8d38..000000000 --- a/rpc/types_test.go +++ /dev/null @@ -1,204 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/json" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -func TestInvalidTypeError(t *testing.T) { - err := NewInvalidTypeError("testField", "not string") - expected := "invalid type on field testField: not string" - - if err.Error() != expected { - t.Error(err.Error()) - } -} - -func TestInsufficientParamsError(t *testing.T) { - err := NewInsufficientParamsError(0, 1) - expected := "insufficient params, want 1 have 0" - - if err.Error() != expected { - t.Error(err.Error()) - } -} - -func TestNotImplementedError(t *testing.T) { - err := NewNotImplementedError("foo") - expected := "foo method not implemented" - - if err.Error() != expected { - t.Error(err.Error()) - } -} - -func TestDecodeParamError(t *testing.T) { - err := NewDecodeParamError("foo") - expected := "could not decode, foo" - - if err.Error() != expected { - t.Error(err.Error()) - } -} - -func TestValidationError(t *testing.T) { - err := NewValidationError("foo", "should be `bar`") - expected := "foo not valid, should be `bar`" - - if err.Error() != expected { - t.Error(err.Error()) - } -} - -func TestHexdataMarshalNil(t *testing.T) { - hd := newHexData([]byte{}) - hd.isNil = true - v, _ := json.Marshal(hd) - if string(v) != "null" { - t.Errorf("Expected null, got %s", v) - } -} - -func TestHexnumMarshalNil(t *testing.T) { - hn := newHexNum([]byte{}) - hn.isNil = true - v, _ := json.Marshal(hn) - if string(v) != "null" { - t.Errorf("Expected null, got %s", v) - } -} - -func TestHexdataNil(t *testing.T) { - v := newHexData(nil) - if v.isNil != true { - t.Errorf("Expected isNil to be true, but is %v", v.isNil) - } -} - -func TestHexdataPtrHash(t *testing.T) { - in := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31} - v := newHexData(&in) - if bytes.Compare(in.Bytes(), v.data) != 0 { - t.Errorf("Got % x expected % x", in, v.data) - } -} - -func TestHexdataPtrHashNil(t *testing.T) { - var in *common.Hash - in = nil - v := newHexData(in) - if !v.isNil { - t.Errorf("Expect isNil to be true, but is %v", v.isNil) - } -} - -func TestHexdataPtrAddress(t *testing.T) { - in := common.Address{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} - v := newHexData(&in) - if bytes.Compare(in.Bytes(), v.data) != 0 { - t.Errorf("Got % x expected % x", in, v.data) - } -} - -func TestHexdataPtrAddressNil(t *testing.T) { - var in *common.Address - in = nil - v := newHexData(in) - if !v.isNil { - t.Errorf("Expect isNil to be true, but is %v", v.isNil) - } -} - -func TestHexdataPtrBloom(t *testing.T) { - in := types.Bloom{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} - v := newHexData(&in) - if bytes.Compare(in.Bytes(), v.data) != 0 { - t.Errorf("Got % x expected % x", in, v.data) - } -} - -func TestHexdataPtrBloomNil(t *testing.T) { - var in *types.Bloom - in = nil - v := newHexData(in) - if !v.isNil { - t.Errorf("Expect isNil to be true, but is %v", v.isNil) - } -} - -func TestHexdataBigintNil(t *testing.T) { - var in *big.Int - in = nil - v := newHexData(in) - if !v.isNil { - t.Errorf("Expect isNil to be true, but is %v", v.isNil) - } -} - -func TestHexdataUint(t *testing.T) { - var in = uint(16) - var expected = []byte{0x10} - v := newHexData(in) - if bytes.Compare(expected, v.data) != 0 { - t.Errorf("Expected % x got % x", expected, v.data) - } -} - -func TestHexdataInt8(t *testing.T) { - var in = int8(16) - var expected = []byte{0x10} - v := newHexData(in) - if bytes.Compare(expected, v.data) != 0 { - t.Errorf("Expected % x got % x", expected, v.data) - } -} - -func TestHexdataUint8(t *testing.T) { - var in = uint8(16) - var expected = []byte{0x10} - v := newHexData(in) - if bytes.Compare(expected, v.data) != 0 { - t.Errorf("Expected % x got % x", expected, v.data) - } -} - -func TestHexdataInt16(t *testing.T) { - var in = int16(16) - var expected = []byte{0x10} - v := newHexData(in) - if bytes.Compare(expected, v.data) != 0 { - t.Errorf("Expected % x got % x", expected, v.data) - } -} - -func TestHexdataUint16(t *testing.T) { - var in = uint16(16) - var expected = []byte{0x0, 0x10} - v := newHexData(in) - if bytes.Compare(expected, v.data) != 0 { - t.Errorf("Expected % x got % x", expected, v.data) - } -} - -func TestHexdataInt32(t *testing.T) { - var in = int32(16) - var expected = []byte{0x10} - v := newHexData(in) - if bytes.Compare(expected, v.data) != 0 { - t.Errorf("Expected % x got % x", expected, v.data) - } -} - -func TestHexdataUint32(t *testing.T) { - var in = uint32(16) - var expected = []byte{0x0, 0x0, 0x0, 0x10} - v := newHexData(in) - if bytes.Compare(expected, v.data) != 0 { - t.Errorf("Expected % x got % x", expected, v.data) - } -}