From 2e0694b606ba6cb2fc78dbda641deb1e8d156024 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 9 Jun 2015 09:47:21 -0400 Subject: Updated test --- rpc/args_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'rpc') diff --git a/rpc/args_test.go b/rpc/args_test.go index fc10d68cf..81a2972cd 100644 --- a/rpc/args_test.go +++ b/rpc/args_test.go @@ -2519,6 +2519,14 @@ func TestSigArgs(t *testing.T) { if err := json.Unmarshal([]byte(input), &args); err != nil { t.Error(err) } + + if expected.From != args.From { + t.Errorf("From should be %v but is %v", expected.From, args.From) + } + + if expected.Data != args.Data { + t.Errorf("Data should be %v but is %v", expected.Data, args.Data) + } } func TestSigArgsEmptyData(t *testing.T) { -- cgit v1.2.3 From 2f55a1d79853c1348fb1a4332fff98110167da80 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 10:23:54 +0200 Subject: restructured eth rpc API --- rpc/api/api.go | 12 + rpc/api/api_test.go | 42 +++ rpc/api/eth.go | 523 ++++++++++++++++++++++++++++++++ rpc/api/eth_args.go | 835 +++++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/eth_js.go | 3 + rpc/api/parsing.go | 460 ++++++++++++++++++++++++++++ rpc/api/utils.go | 36 +++ rpc/codec/codec.go | 47 +++ rpc/codec/json.go | 75 +++++ rpc/shared/errors.go | 96 ++++++ rpc/shared/types.go | 38 +++ 11 files changed, 2167 insertions(+) create mode 100644 rpc/api/api.go create mode 100644 rpc/api/api_test.go create mode 100644 rpc/api/eth.go create mode 100644 rpc/api/eth_args.go create mode 100644 rpc/api/eth_js.go create mode 100644 rpc/api/parsing.go create mode 100644 rpc/api/utils.go create mode 100644 rpc/codec/codec.go create mode 100644 rpc/codec/json.go create mode 100644 rpc/shared/errors.go create mode 100644 rpc/shared/types.go (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go new file mode 100644 index 000000000..758e056ed --- /dev/null +++ b/rpc/api/api.go @@ -0,0 +1,12 @@ +package api + +import "github.com/ethereum/go-ethereum/rpc/shared" + +// Ethereum RPC API interface +type EthereumApi interface { + // Execute the given request and returns the response or an error + Execute(*shared.Request) (interface{}, error) + + // List of supported RCP methods this API provides + Methods() []string +} diff --git a/rpc/api/api_test.go b/rpc/api/api_test.go new file mode 100644 index 000000000..f1a47944d --- /dev/null +++ b/rpc/api/api_test.go @@ -0,0 +1,42 @@ +package api + +import ( + "testing" + + "github.com/ethereum/go-ethereum/rpc/codec" +) + +func TestParseApiString(t *testing.T) { + apis, err := ParseApiString("", codec.JSON, nil, nil) + if err == nil { + t.Errorf("Expected an err from parsing empty API string but got nil") + } + + if len(apis) != 0 { + t.Errorf("Expected 0 apis from empty API string") + } + + apis, err = ParseApiString("eth", codec.JSON, nil, nil) + if err != nil { + t.Errorf("Expected nil err from parsing empty API string but got %v", err) + } + + if len(apis) != 1 { + t.Errorf("Expected 1 apis but got %d - %v", apis, apis) + } + + apis, err = ParseApiString("eth,eth", codec.JSON, nil, nil) + if err != nil { + t.Errorf("Expected nil err from parsing empty API string but got \"%v\"", err) + } + + if len(apis) != 2 { + t.Errorf("Expected 2 apis but got %d - %v", apis, apis) + } + + apis, err = ParseApiString("eth,invalid", codec.JSON, nil, nil) + if err == nil { + t.Errorf("Expected an err but got no err") + } + +} diff --git a/rpc/api/eth.go b/rpc/api/eth.go new file mode 100644 index 000000000..fa14aa41e --- /dev/null +++ b/rpc/api/eth.go @@ -0,0 +1,523 @@ +package api + +import ( + "bytes" + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +// eth api provider +// See https://github.com/ethereum/wiki/wiki/JSON-RPC +type EthApi struct { + xeth *xeth.XEth + methods map[string]ethhandler + codec codec.ApiCoder +} + +// eth callback handler +type ethhandler func(*EthApi, *shared.Request) (interface{}, error) + +var ( + ethMapping = map[string]ethhandler{ + "eth_accounts": (*EthApi).Accounts, + "eth_blockNumber": (*EthApi).BlockNumber, + "eth_getBalance": (*EthApi).GetBalance, + "eth_protocolVersion": (*EthApi).ProtocolVersion, + "eth_coinbase": (*EthApi).Coinbase, + "eth_mining": (*EthApi).IsMining, + "eth_gasPrice": (*EthApi).GasPrice, + "eth_getStorage": (*EthApi).GetStorage, + "eth_storageAt": (*EthApi).GetStorage, + "eth_getStorageAt": (*EthApi).GetStorageAt, + "eth_getTransactionCount": (*EthApi).GetTransactionCount, + "eth_getBlockTransactionCountByHash": (*EthApi).GetBlockTransactionCountByHash, + "eth_getBlockTransactionCountByNumber": (*EthApi).GetBlockTransactionCountByNumber, + "eth_getUncleCountByBlockHash": (*EthApi).GetUncleCountByBlockHash, + "eth_getUncleCountByBlockNumber": (*EthApi).GetUncleCountByBlockNumber, + "eth_getData": (*EthApi).GetData, + "eth_getCode": (*EthApi).GetData, + "eth_sign": (*EthApi).Sign, + "eth_sendTransaction": (*EthApi).SendTransaction, + "eth_transact": (*EthApi).SendTransaction, + "eth_estimateGas": (*EthApi).EstimateGas, + "eth_call": (*EthApi).Call, + "eth_flush": (*EthApi).Flush, + "eth_getBlockByHash": (*EthApi).GetBlockByHash, + "eth_getBlockByNumber": (*EthApi).GetBlockByNumber, + "eth_getTransactionByHash": (*EthApi).GetTransactionByHash, + "eth_getTransactionByBlockHashAndIndex": (*EthApi).GetTransactionByBlockHashAndIndex, + "eth_getUncleByBlockHashAndIndex": (*EthApi).GetUncleByBlockHashAndIndex, + "eth_getUncleByBlockNumberAndIndex": (*EthApi).GetUncleByBlockNumberAndIndex, + "eth_getCompilers": (*EthApi).GetCompilers, + "eth_compileSolidity": (*EthApi).CompileSolidity, + "eth_newFilter": (*EthApi).NewFilter, + "eth_newBlockFilter": (*EthApi).NewBlockFilter, + "eth_newPendingTransactionFilter": (*EthApi).NewPendingTransactionFilter, + "eth_uninstallFilter": (*EthApi).UninstallFilter, + "eth_getFilterChanges": (*EthApi).GetFilterChanges, + "eth_getFilterLogs": (*EthApi).GetFilterLogs, + "eth_getLogs": (*EthApi).GetLogs, + "eth_hashrate": (*EthApi).Hashrate, + "eth_getWork": (*EthApi).GetWork, + "eth_submitWork": (*EthApi).SubmitWork, + } +) + +// create new EthApi instance +func NewEthApi(xeth *xeth.XEth, codec codec.Codec) *EthApi { + return &EthApi{xeth, ethMapping, codec.New(nil)} +} + +// collection with supported methods +func (self *EthApi) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *EthApi) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, shared.NewNotImplementedError(req.Method) +} + +func (self *EthApi) Accounts(req *shared.Request) (interface{}, error) { + return self.xeth.Accounts(), nil +} + +func (self *EthApi) Hashrate(req *shared.Request) (interface{}, error) { + return newHexNum(self.xeth.HashRate()), nil +} + +func (self *EthApi) BlockNumber(req *shared.Request) (interface{}, error) { + return self.xeth.CurrentBlock().Number(), nil +} + +func (self *EthApi) GetBalance(req *shared.Request) (interface{}, error) { + args := new(GetBalanceArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil +} + +func (self *EthApi) ProtocolVersion(req *shared.Request) (interface{}, error) { + return self.xeth.EthVersion(), nil +} + +func (self *EthApi) Coinbase(req *shared.Request) (interface{}, error) { + return newHexData(self.xeth.Coinbase()), nil +} + +func (self *EthApi) IsMining(req *shared.Request) (interface{}, error) { + return self.xeth.IsMining(), nil +} + +func (self *EthApi) GasPrice(req *shared.Request) (interface{}, error) { + return newHexNum(xeth.DefaultGasPrice().Bytes()), nil +} + +func (self *EthApi) GetStorage(req *shared.Request) (interface{}, error) { + args := new(GetStorageArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil +} + +func (self *EthApi) GetStorageAt(req *shared.Request) (interface{}, error) { + args := new(GetStorageAtArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil +} + +func (self *EthApi) GetTransactionCount(req *shared.Request) (interface{}, error) { + args := new(GetTxCountArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + count := self.xeth.AtStateNum(args.BlockNumber).TxCountAt(args.Address) + return newHexNum(big.NewInt(int64(count)).Bytes()), nil +} + +func (self *EthApi) GetBlockTransactionCountByHash(req *shared.Request) (interface{}, error) { + args := new(HashArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := NewBlockRes(self.xeth.EthBlockByHash(args.Hash), false) + if block == nil { + return nil, nil + } else { + return newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes()), nil + } +} + +func (self *EthApi) GetBlockTransactionCountByNumber(req *shared.Request) (interface{}, error) { + args := new(BlockNumArg) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := NewBlockRes(self.xeth.EthBlockByNumber(args.BlockNumber), false) + if block == nil { + return nil, nil + } else { + return newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes()), nil + } +} + +func (self *EthApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, error) { + args := new(HashArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByHash(args.Hash) + br := NewBlockRes(block, false) + if br == nil { + return nil, nil + } + return newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes()), nil +} + +func (self *EthApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}, error) { + args := new(BlockNumArg) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByNumber(args.BlockNumber) + br := NewBlockRes(block, false) + if br == nil { + return nil, nil + } + return newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes()), nil +} + +func (self *EthApi) GetData(req *shared.Request) (interface{}, error) { + args := new(GetDataArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + v := self.xeth.AtStateNum(args.BlockNumber).CodeAtBytes(args.Address) + return newHexData(v), nil +} + +func (self *EthApi) Sign(req *shared.Request) (interface{}, error) { + args := new(NewSignArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + v, err := self.xeth.Sign(args.From, args.Data, false) + if err != nil { + return nil, err + } + return v, nil +} + +func (self *EthApi) SendTransaction(req *shared.Request) (interface{}, error) { + args := new(NewTxArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + // nonce may be nil ("guess" mode) + var nonce string + if args.Nonce != nil { + nonce = args.Nonce.String() + } + + v, err := self.xeth.Transact(args.From, args.To, nonce, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) + if err != nil { + return nil, err + } + return v, nil +} + +func (self *EthApi) EstimateGas(req *shared.Request) (interface{}, error) { + _, gas, err := self.doCall(req.Params) + if err != nil { + return nil, err + } + + // TODO unwrap the parent method's ToHex call + if len(gas) == 0 { + return newHexNum(0), nil + } else { + return newHexNum(gas), nil + } +} + +func (self *EthApi) Call(req *shared.Request) (interface{}, error) { + v, _, err := self.doCall(req.Params) + if err != nil { + return nil, err + } + + // TODO unwrap the parent method's ToHex call + if v == "0x0" { + return newHexData([]byte{}), nil + } else { + return newHexData(common.FromHex(v)), nil + } +} + +func (self *EthApi) Flush(req *shared.Request) (interface{}, error) { + return nil, shared.NewNotImplementedError(req.Method) +} + +func (self *EthApi) doCall(params json.RawMessage) (string, string, error) { + args := new(CallArgs) + if err := self.codec.Decode(params, &args); err != nil { + return "", "", err + } + + return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) +} + +func (self *EthApi) GetBlockByHash(req *shared.Request) (interface{}, error) { + args := new(GetBlockByHashArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByHash(args.BlockHash) + return NewBlockRes(block, args.IncludeTxs), nil +} + +func (self *EthApi) GetBlockByNumber(req *shared.Request) (interface{}, error) { + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.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 + } + return br, nil +} + +func (self *EthApi) GetTransactionByHash(req *shared.Request) (interface{}, error) { + args := new(HashArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + tx, bhash, bnum, txi := self.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) + } + return v, nil + } + return nil, nil +} + +func (self *EthApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (interface{}, error) { + args := new(HashIndexArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByHash(args.Hash) + br := NewBlockRes(block, true) + if br == nil { + return nil, nil + } + + if args.Index >= int64(len(br.Transactions)) || args.Index < 0 { + return nil, nil + } else { + return br.Transactions[args.Index], nil + } +} + +func (self *EthApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { + args := new(BlockNumIndexArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByNumber(args.BlockNumber) + v := NewBlockRes(block, true) + if v == nil { + return nil, nil + } + + if args.Index >= int64(len(v.Transactions)) || args.Index < 0 { + // return NewValidationError("Index", "does not exist") + return nil, nil + } + return v.Transactions[args.Index], nil +} + +func (self *EthApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{}, error) { + args := new(HashIndexArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + br := NewBlockRes(self.xeth.EthBlockByHash(args.Hash), false) + if br == nil { + return nil, nil + } + + if args.Index >= int64(len(br.Uncles)) || args.Index < 0 { + // return NewValidationError("Index", "does not exist") + return nil, nil + } + + return br.Uncles[args.Index], nil +} + +func (self *EthApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { + args := new(BlockNumIndexArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByNumber(args.BlockNumber) + v := NewBlockRes(block, true) + + if v == nil { + return nil, nil + } + + if args.Index >= int64(len(v.Uncles)) || args.Index < 0 { + return nil, nil + } else { + return v.Uncles[args.Index], nil + } +} + +func (self *EthApi) GetCompilers(req *shared.Request) (interface{}, error) { + var lang string + if solc, _ := self.xeth.Solc(); solc != nil { + lang = "Solidity" + } + c := []string{lang} + return c, nil +} + +func (self *EthApi) CompileSolidity(req *shared.Request) (interface{}, error) { + solc, _ := self.xeth.Solc() + if solc == nil { + return nil, shared.NewNotAvailableError(req.Method, "solc (solidity compiler) not found") + } + + args := new(SourceArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + contracts, err := solc.Compile(args.Source) + if err != nil { + return nil, err + } + return contracts, nil +} + +func (self *EthApi) NewFilter(req *shared.Request) (interface{}, error) { + args := new(BlockFilterArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + id := self.xeth.NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics) + return newHexNum(big.NewInt(int64(id)).Bytes()), nil +} + +func (self *EthApi) NewBlockFilter(req *shared.Request) (interface{}, error) { + return newHexNum(self.xeth.NewBlockFilter()), nil +} + +func (self *EthApi) NewPendingTransactionFilter(req *shared.Request) (interface{}, error) { + return newHexNum(self.xeth.NewTransactionFilter()), nil +} + +func (self *EthApi) UninstallFilter(req *shared.Request) (interface{}, error) { + args := new(FilterIdArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + return self.xeth.UninstallFilter(args.Id), nil +} + +func (self *EthApi) GetFilterChanges(req *shared.Request) (interface{}, error) { + args := new(FilterIdArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + switch self.xeth.GetFilterType(args.Id) { + case xeth.BlockFilterTy: + return NewHashesRes(self.xeth.BlockFilterChanged(args.Id)), nil + case xeth.TransactionFilterTy: + return NewHashesRes(self.xeth.TransactionFilterChanged(args.Id)), nil + case xeth.LogFilterTy: + return NewLogsRes(self.xeth.LogFilterChanged(args.Id)), nil + default: + return []string{}, nil // reply empty string slice + } +} + +func (self *EthApi) GetFilterLogs(req *shared.Request) (interface{}, error) { + args := new(FilterIdArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + return NewLogsRes(self.xeth.Logs(args.Id)), nil +} + +func (self *EthApi) GetLogs(req *shared.Request) (interface{}, error) { + args := new(BlockFilterArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + return NewLogsRes(self.xeth.AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)), nil +} + +func (self *EthApi) GetWork(req *shared.Request) (interface{}, error) { + self.xeth.SetMining(true, 0) + return self.xeth.RemoteMining().GetWork(), nil +} + +func (self *EthApi) SubmitWork(req *shared.Request) (interface{}, error) { + args := new(SubmitWorkArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + return self.xeth.RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)), nil +} diff --git a/rpc/api/eth_args.go b/rpc/api/eth_args.go new file mode 100644 index 000000000..1ef6f9efb --- /dev/null +++ b/rpc/api/eth_args.go @@ -0,0 +1,835 @@ +package api + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +const ( + defaultLogLimit = 100 + defaultLogOffset = 0 +) + +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 shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type GetStorageArgs struct { + Address string + BlockNumber int64 +} + +func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type GetStorageAtArgs struct { + Address string + BlockNumber int64 + Key string +} + +func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + keystr, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("key", "not a string") + } + args.Key = keystr + + if len(obj) > 2 { + if err := blockHeight(obj[2], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type GetTxCountArgs struct { + Address string + BlockNumber int64 +} + +func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type HashArgs struct { + Hash string +} + +func (args *HashArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + arg0, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("hash", "not a string") + } + args.Hash = arg0 + + return nil +} + +type BlockNumArg struct { + BlockNumber int64 +} + +func (args *BlockNumArg) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + if err := blockHeight(obj[0], &args.BlockNumber); err != nil { + return err + } + + return nil +} + +type GetDataArgs struct { + Address string + BlockNumber int64 +} + +func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type NewSignArgs struct { + From string + Data string +} + +func (args *NewSignArgs) UnmarshalJSON(b []byte) (err error) { + var obj []json.RawMessage + var ext struct { + From string + Data string + } + + // Decode byte slice to array of RawMessages + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + // Check for sufficient params + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + // Decode 0th RawMessage to temporary struct + if err := json.Unmarshal(obj[0], &ext); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(ext.From) == 0 { + return shared.NewValidationError("from", "is required") + } + + if len(ext.Data) == 0 { + return shared.NewValidationError("data", "is required") + } + + args.From = ext.From + args.Data = ext.Data + return nil +} + +type NewTxArgs struct { + From string + To string + Nonce *big.Int + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data string + + BlockNumber int64 +} + +func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { + var obj []json.RawMessage + var ext struct { + From string + To string + Nonce interface{} + Value interface{} + Gas interface{} + GasPrice interface{} + Data string + } + + // Decode byte slice to array of RawMessages + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + // Check for sufficient params + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + // Decode 0th RawMessage to temporary struct + if err := json.Unmarshal(obj[0], &ext); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(ext.From) == 0 { + return shared.NewValidationError("from", "is required") + } + + 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 { + num = big.NewInt(0) + } else { + if num, err = numString(ext.Gas); err != nil { + return err + } + } + args.Gas = num + + num = nil + if ext.GasPrice == nil { + num = big.NewInt(0) + } else { + if num, err = numString(ext.GasPrice); err != nil { + return err + } + } + 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 SourceArgs struct { + Source string +} + +func (args *SourceArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + arg0, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("source code", "not a string") + } + args.Source = arg0 + + return nil +} + +type CallArgs struct { + From string + To string + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data string + + BlockNumber int64 +} + +func (args *CallArgs) UnmarshalJSON(b []byte) (err error) { + var obj []json.RawMessage + var ext struct { + From string + To string + Value interface{} + Gas interface{} + GasPrice interface{} + Data string + } + + // Decode byte slice to array of RawMessages + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + // Check for sufficient params + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + // Decode 0th RawMessage to temporary struct + if err := json.Unmarshal(obj[0], &ext); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + args.From = ext.From + + if len(ext.To) == 0 { + return shared.NewValidationError("to", "is required") + } + args.To = ext.To + + var num *big.Int + if ext.Value == nil { + num = big.NewInt(0) + } else { + if num, err = numString(ext.Value); err != nil { + return err + } + } + args.Value = num + + if ext.Gas == nil { + num = big.NewInt(0) + } else { + if num, err = numString(ext.Gas); err != nil { + return err + } + } + args.Gas = num + + if ext.GasPrice == nil { + num = big.NewInt(0) + } else { + if num, err = numString(ext.GasPrice); err != nil { + return err + } + } + args.GasPrice = num + + args.Data = ext.Data + + // Check for optional BlockNumber param + if len(obj) > 1 { + if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type HashIndexArgs struct { + Hash string + Index int64 +} + +func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + arg0, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("hash", "not a string") + } + args.Hash = arg0 + + arg1, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("index", "not a string") + } + args.Index = common.Big(arg1).Int64() + + return nil +} + +type BlockNumIndexArgs struct { + BlockNumber int64 + Index int64 +} + +func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + if err := blockHeight(obj[0], &args.BlockNumber); err != nil { + return err + } + + var arg1 *big.Int + if arg1, err = numString(obj[1]); err != nil { + return err + } + args.Index = arg1.Int64() + + return nil +} + +type GetBlockByHashArgs struct { + BlockHash string + IncludeTxs bool +} + +func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + argstr, ok := obj[0].(string) + if !ok { + return shared.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 shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + if err := blockHeight(obj[0], &args.BlockNumber); err != nil { + return err + } + + args.IncludeTxs = obj[1].(bool) + + 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 shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + // args.Earliest, err = toNumber(obj[0].ToBlock) + // if err != nil { + // return shared.NewDecodeParamError(fmt.Sprintf("FromBlock %v", err)) + // } + // args.Latest, err = toNumber(obj[0].FromBlock) + // if err != nil { + // return shared.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 shared.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 shared.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 shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string") + } + } + } else if iv == nil { + topicdbl[i] = []string{""} + } else { + return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array") + } + } + args.Topics = topicdbl + return nil + } else { + return shared.NewInvalidTypeError("topic", "is not a string or array") + } + } + + 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 shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.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 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 +} + +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 shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 3 { + return shared.NewInsufficientParamsError(len(obj), 3) + } + + var objstr string + var ok bool + if objstr, ok = obj[0].(string); !ok { + return shared.NewInvalidTypeError("nonce", "not a string") + } + + args.Nonce = common.String2Big(objstr).Uint64() + if objstr, ok = obj[1].(string); !ok { + return shared.NewInvalidTypeError("header", "not a string") + } + + args.Header = objstr + + if objstr, ok = obj[2].(string); !ok { + return shared.NewInvalidTypeError("digest", "not a string") + } + + args.Digest = objstr + + return nil +} diff --git a/rpc/api/eth_js.go b/rpc/api/eth_js.go new file mode 100644 index 000000000..f7630bdd5 --- /dev/null +++ b/rpc/api/eth_js.go @@ -0,0 +1,3 @@ +package api + +// JS api provided by web3.js diff --git a/rpc/api/parsing.go b/rpc/api/parsing.go new file mode 100644 index 000000000..85a9165e5 --- /dev/null +++ b/rpc/api/parsing.go @@ -0,0 +1,460 @@ +package api + +import ( + "encoding/binary" + "encoding/hex" + "encoding/json" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +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 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"` +// } + +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, shared.NewInvalidTypeError("", "not a number or string") +} + +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 shared.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 shared.NewInvalidTypeError("blockNumber", "is not a valid string") + } + } + + return nil +} + +func blockHeightFromJson(msg json.RawMessage, number *int64) error { + var raw interface{} + if err := json.Unmarshal(msg, &raw); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + return blockHeight(raw, number) +} diff --git a/rpc/api/utils.go b/rpc/api/utils.go new file mode 100644 index 000000000..76f00c251 --- /dev/null +++ b/rpc/api/utils.go @@ -0,0 +1,36 @@ +package api + +import ( + "strings" + + "fmt" + + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/xeth" +) + +const ( + EthApiName = "eth" +) + +// Parse a comma separated API string to individual api's +func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.Ethereum) ([]EthereumApi, error) { + if len(strings.TrimSpace(apistr)) == 0 { + return nil, fmt.Errorf("Empty apistr provided") + } + + names := strings.Split(apistr, ",") + apis := make([]EthereumApi, len(names)) + + for i, name := range names { + switch strings.ToLower(strings.TrimSpace(name)) { + case EthApiName: + apis[i] = NewEthApi(xeth, codec) + default: + return nil, fmt.Errorf("Unknown API '%s'", name) + } + } + + return apis, nil +} diff --git a/rpc/codec/codec.go b/rpc/codec/codec.go new file mode 100644 index 000000000..5e8f38438 --- /dev/null +++ b/rpc/codec/codec.go @@ -0,0 +1,47 @@ +package codec + +import ( + "net" + "strconv" + + "github.com/ethereum/go-ethereum/rpc/shared" +) + +type Codec int + +// (de)serialization support for rpc interface +type ApiCoder interface { + // Parse message to request from underlying stream + ReadRequest() (*shared.Request, error) + // Parse response message from underlying stream + ReadResponse() (interface{}, error) + // Encode response to encoded form in underlying stream + WriteResponse(interface{}) error + // Decode single message from data + Decode([]byte, interface{}) error + // Encode msg to encoded form + Encode(msg interface{}) ([]byte, error) + // close the underlying stream + Close() +} + +// supported codecs +const ( + JSON Codec = iota + nCodecs +) + +var ( + // collection with supported coders + coders = make([]func(net.Conn) ApiCoder, nCodecs) +) + +// create a new coder instance +func (c Codec) New(conn net.Conn) ApiCoder { + switch c { + case JSON: + return NewJsonCoder(conn) + } + + panic("codec: request for codec #" + strconv.Itoa(int(c)) + " is unavailable") +} diff --git a/rpc/codec/json.go b/rpc/codec/json.go new file mode 100644 index 000000000..31024ee74 --- /dev/null +++ b/rpc/codec/json.go @@ -0,0 +1,75 @@ +package codec + +import ( + "encoding/json" + "net" + + "github.com/ethereum/go-ethereum/rpc/shared" +) + +const ( + MAX_RESPONSE_SIZE = 64 * 1024 +) + +// Json serialization support +type JsonCodec struct { + c net.Conn + d *json.Decoder + e *json.Encoder +} + +// Create new JSON coder instance +func NewJsonCoder(conn net.Conn) ApiCoder { + return &JsonCodec{ + c: conn, + d: json.NewDecoder(conn), + e: json.NewEncoder(conn), + } +} + +// Serialize obj to JSON and write it to conn +func (self *JsonCodec) ReadRequest() (*shared.Request, error) { + req := shared.Request{} + err := self.d.Decode(&req) + if err == nil { + return &req, nil + } + return nil, err +} + +func (self *JsonCodec) ReadResponse() (interface{}, error) { + var err error + buf := make([]byte, MAX_RESPONSE_SIZE) + n, _ := self.c.Read(buf) + + var failure shared.ErrorResponse + if err = json.Unmarshal(buf[:n], &failure); err == nil && failure.Error != nil { + return failure, nil + } + + var success shared.SuccessResponse + if err = json.Unmarshal(buf[:n], &success); err == nil { + return success, nil + } + + return nil, err +} + +// Encode response to encoded form in underlying stream +func (self *JsonCodec) Decode(data []byte, msg interface{}) error { + return json.Unmarshal(data, msg) +} + +func (self *JsonCodec) Encode(msg interface{}) ([]byte, error) { + return json.Marshal(msg) +} + +// Parse JSON data from conn to obj +func (self *JsonCodec) WriteResponse(res interface{}) error { + return self.e.Encode(&res) +} + +// Close decoder and encoder +func (self *JsonCodec) Close() { + self.c.Close() +} diff --git a/rpc/shared/errors.go b/rpc/shared/errors.go new file mode 100644 index 000000000..bd10b33a0 --- /dev/null +++ b/rpc/shared/errors.go @@ -0,0 +1,96 @@ +package shared + +import "fmt" + +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 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 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, + } +} diff --git a/rpc/shared/types.go b/rpc/shared/types.go new file mode 100644 index 000000000..46fd5552c --- /dev/null +++ b/rpc/shared/types.go @@ -0,0 +1,38 @@ +package shared + +import "encoding/json" + +// RPC request +type Request struct { + Id interface{} `json:"id"` + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params json.RawMessage `json:"params"` +} + +// RPC response +type Response struct { + Id interface{} `json:"id"` + Jsonrpc string `json:"jsonrpc"` +} + +// RPC success response +type SuccessResponse struct { + Id interface{} `json:"id"` + Jsonrpc string `json:"jsonrpc"` + Result interface{} `json:"result"` +} + +// RPC error response +type ErrorResponse struct { + Id interface{} `json:"id"` + Jsonrpc string `json:"jsonrpc"` + Error *ErrorObject `json:"error"` +} + +// RPC error response details +type ErrorObject struct { + Code int `json:"code"` + Message string `json:"message"` + // Data interface{} `json:"data"` +} -- cgit v1.2.3 From 8ebf2d8fad729a8261f237bb05b6073e6c1b652f Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 10:41:04 +0200 Subject: added RPC/IPC support --- rpc/api/utils.go | 41 +++ rpc/comms/comms.go | 7 + rpc/comms/ipc.go | 37 +++ rpc/comms/ipc_unix.go | 77 ++++++ rpc/comms/ipc_windows.go | 699 +++++++++++++++++++++++++++++++++++++++++++++++ rpc/shared/types.go | 27 +- 6 files changed, 887 insertions(+), 1 deletion(-) create mode 100644 rpc/comms/comms.go create mode 100644 rpc/comms/ipc.go create mode 100644 rpc/comms/ipc_unix.go create mode 100644 rpc/comms/ipc_windows.go (limited to 'rpc') diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 76f00c251..a62058140 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/xeth" + "github.com/ethereum/go-ethereum/rpc/shared" ) const ( @@ -34,3 +35,43 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. return apis, nil } + +// combines multiple API's +type mergedApi struct { + apis map[string]EthereumApi +} + +// create new merged api instance +func newMergedApi(apis ...EthereumApi) *mergedApi { + mergedApi := new(mergedApi) + mergedApi.apis = make(map[string]EthereumApi) + + for _, api := range apis { + for _, method := range api.Methods() { + mergedApi.apis[method] = api + } + } + return mergedApi +} + +// Supported RPC methods +func (self *mergedApi) Methods() []string { + all := make([]string, len(self.apis)) + for method, _ := range self.apis { + all = append(all, method) + } + return all +} + +// Call the correct API's Execute method for the given request +func (self *mergedApi) Execute(req *shared.Request) (interface{}, error) { + if api, found := self.apis[req.Method]; found { + return api.Execute(req) + } + return nil, shared.NewNotImplementedError(req.Method) +} + +// Merge multiple API's to a single API instance +func Merge(apis ...EthereumApi) EthereumApi { + return newMergedApi(apis...) +} diff --git a/rpc/comms/comms.go b/rpc/comms/comms.go new file mode 100644 index 000000000..244f5a7a6 --- /dev/null +++ b/rpc/comms/comms.go @@ -0,0 +1,7 @@ +package comms + +type EthereumClient interface { + Close() + Send(interface{}) error + Recv() (interface{}, error) +} diff --git a/rpc/comms/ipc.go b/rpc/comms/ipc.go new file mode 100644 index 000000000..a75039d17 --- /dev/null +++ b/rpc/comms/ipc.go @@ -0,0 +1,37 @@ +package comms + +import ( + "github.com/ethereum/go-ethereum/rpc/api" + "github.com/ethereum/go-ethereum/rpc/codec" +) + +type IpcConfig struct { + Endpoint string +} + +type ipcClient struct { + c codec.ApiCoder +} + +func (self *ipcClient) Close() { + self.c.Close() +} + +func (self *ipcClient) Send(req interface{}) error { + return self.c.WriteResponse(req) +} + +func (self *ipcClient) Recv() (interface{}, error) { + return self.c.ReadResponse() +} + +// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows +func NewIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { + return newIpcClient(cfg, codec) +} + +// Start IPC server +func StartIpc(cfg IpcConfig, codec codec.Codec, apis ...api.EthereumApi) error { + offeredApi := api.Merge(apis...) + return startIpc(cfg, codec, offeredApi) +} diff --git a/rpc/comms/ipc_unix.go b/rpc/comms/ipc_unix.go new file mode 100644 index 000000000..bb09d9547 --- /dev/null +++ b/rpc/comms/ipc_unix.go @@ -0,0 +1,77 @@ +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package comms + +import ( + "io" + "net" + "os" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rpc/api" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { + c, err := net.DialUnix("unix", nil, &net.UnixAddr{cfg.Endpoint, "unix"}) + if err != nil { + return nil, err + } + + return &ipcClient{codec.New(c)}, nil +} + +func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { + os.Remove(cfg.Endpoint) // in case it still exists from a previous run + + l, err := net.ListenUnix("unix", &net.UnixAddr{Name: cfg.Endpoint, Net: "unix"}) + if err != nil { + return err + } + os.Chmod(cfg.Endpoint, 0600) + + go func() { + for { + conn, err := l.AcceptUnix() + if err != nil { + glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err) + continue + } + + go func(conn net.Conn) { + codec := codec.New(conn) + + for { + req, err := codec.ReadRequest() + if err == io.EOF { + codec.Close() + return + } else if err != nil { + glog.V(logger.Error).Infof("IPC recv err - %v\n", err) + codec.Close() + return + } + + var rpcResponse interface{} + res, err := api.Execute(req) + + rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) + err = codec.WriteResponse(rpcResponse) + if err != nil { + glog.V(logger.Error).Infof("IPC send err - %v\n", err) + codec.Close() + return + } + } + }(conn) + } + + os.Remove(cfg.Endpoint) + }() + + glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) + + return nil +} diff --git a/rpc/comms/ipc_windows.go b/rpc/comms/ipc_windows.go new file mode 100644 index 000000000..ff9015d03 --- /dev/null +++ b/rpc/comms/ipc_windows.go @@ -0,0 +1,699 @@ +// +build windows + +package comms + +import ( + "fmt" + "io" + "net" + "os" + "sync" + "syscall" + "time" + "unsafe" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rpc/api" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") + procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") + procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") + procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") +) + +func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func disconnectNamedPipe(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func waitNamedPipe(name *uint16, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if initialState { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + + +const ( + // openMode + pipe_access_duplex = 0x3 + pipe_access_inbound = 0x1 + pipe_access_outbound = 0x2 + + // openMode write flags + file_flag_first_pipe_instance = 0x00080000 + file_flag_write_through = 0x80000000 + file_flag_overlapped = 0x40000000 + + // openMode ACL flags + write_dac = 0x00040000 + write_owner = 0x00080000 + access_system_security = 0x01000000 + + // pipeMode + pipe_type_byte = 0x0 + pipe_type_message = 0x4 + + // pipeMode read mode flags + pipe_readmode_byte = 0x0 + pipe_readmode_message = 0x2 + + // pipeMode wait mode flags + pipe_wait = 0x0 + pipe_nowait = 0x1 + + // pipeMode remote-client mode flags + pipe_accept_remote_clients = 0x0 + pipe_reject_remote_clients = 0x8 + + pipe_unlimited_instances = 255 + + nmpwait_wait_forever = 0xFFFFFFFF + + // the two not-an-errors below occur if a client connects to the pipe between + // the server's CreateNamedPipe and ConnectNamedPipe calls. + error_no_data syscall.Errno = 0xE8 + error_pipe_connected syscall.Errno = 0x217 + error_pipe_busy syscall.Errno = 0xE7 + error_sem_timeout syscall.Errno = 0x79 + + error_bad_pathname syscall.Errno = 0xA1 + error_invalid_name syscall.Errno = 0x7B + + error_io_incomplete syscall.Errno = 0x3e4 +) + +var _ net.Conn = (*PipeConn)(nil) +var _ net.Listener = (*PipeListener)(nil) + +// ErrClosed is the error returned by PipeListener.Accept when Close is called +// on the PipeListener. +var ErrClosed = PipeError{"Pipe has been closed.", false} + +// PipeError is an error related to a call to a pipe +type PipeError struct { + msg string + timeout bool +} + +// Error implements the error interface +func (e PipeError) Error() string { + return e.msg +} + +// Timeout implements net.AddrError.Timeout() +func (e PipeError) Timeout() bool { + return e.timeout +} + +// Temporary implements net.AddrError.Temporary() +func (e PipeError) Temporary() bool { + return false +} + +// Dial connects to a named pipe with the given address. If the specified pipe is not available, +// it will wait indefinitely for the pipe to become available. +// +// The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ +// for remote pipes. +// +// Dial will return a PipeError if you pass in a badly formatted pipe name. +// +// Examples: +// // local pipe +// conn, err := Dial(`\\.\pipe\mypipename`) +// +// // remote pipe +// conn, err := Dial(`\\othercomp\pipe\mypipename`) +func Dial(address string) (*PipeConn, error) { + for { + conn, err := dial(address, nmpwait_wait_forever) + if err == nil { + return conn, nil + } + if isPipeNotReady(err) { + <-time.After(100 * time.Millisecond) + continue + } + return nil, err + } +} + +// DialTimeout acts like Dial, but will time out after the duration of timeout +func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) { + deadline := time.Now().Add(timeout) + + now := time.Now() + for now.Before(deadline) { + millis := uint32(deadline.Sub(now) / time.Millisecond) + conn, err := dial(address, millis) + if err == nil { + return conn, nil + } + if err == error_sem_timeout { + // This is WaitNamedPipe's timeout error, so we know we're done + return nil, PipeError{fmt.Sprintf( + "Timed out waiting for pipe '%s' to come available", address), true} + } + if isPipeNotReady(err) { + left := deadline.Sub(time.Now()) + retry := 100 * time.Millisecond + if left > retry { + <-time.After(retry) + } else { + <-time.After(left - time.Millisecond) + } + now = time.Now() + continue + } + return nil, err + } + return nil, PipeError{fmt.Sprintf( + "Timed out waiting for pipe '%s' to come available", address), true} +} + +// isPipeNotReady checks the error to see if it indicates the pipe is not ready +func isPipeNotReady(err error) bool { + // Pipe Busy means another client just grabbed the open pipe end, + // and the server hasn't made a new one yet. + // File Not Found means the server hasn't created the pipe yet. + // Neither is a fatal error. + + return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy +} + +// newOverlapped creates a structure used to track asynchronous +// I/O requests that have been issued. +func newOverlapped() (*syscall.Overlapped, error) { + event, err := createEvent(nil, true, true, nil) + if err != nil { + return nil, err + } + return &syscall.Overlapped{HEvent: event}, nil +} + +// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete. +// This function returns the number of bytes transferred by the operation and an error code if +// applicable (nil otherwise). +func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) { + _, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE) + if err != nil { + return 0, err + } + var transferred uint32 + err = getOverlappedResult(handle, overlapped, &transferred, true) + return transferred, err +} + +// dial is a helper to initiate a connection to a named pipe that has been started by a server. +// The timeout is only enforced if the pipe server has already created the pipe, otherwise +// this function will return immediately. +func dial(address string, timeout uint32) (*PipeConn, error) { + name, err := syscall.UTF16PtrFromString(string(address)) + if err != nil { + return nil, err + } + // If at least one instance of the pipe has been created, this function + // will wait timeout milliseconds for it to become available. + // It will return immediately regardless of timeout, if no instances + // of the named pipe have been created yet. + // If this returns with no error, there is a pipe available. + if err := waitNamedPipe(name, timeout); err != nil { + if err == error_bad_pathname { + // badly formatted pipe name + return nil, badAddr(address) + } + return nil, err + } + pathp, err := syscall.UTF16PtrFromString(address) + if err != nil { + return nil, err + } + handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, + uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, + syscall.FILE_FLAG_OVERLAPPED, 0) + if err != nil { + return nil, err + } + return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil +} + +// Listen returns a new PipeListener that will listen on a pipe with the given +// address. The address must be of the form \\.\pipe\ +// +// Listen will return a PipeError for an incorrectly formatted pipe name. +func Listen(address string) (*PipeListener, error) { + handle, err := createPipe(address, true) + if err == error_invalid_name { + return nil, badAddr(address) + } + if err != nil { + return nil, err + } + return &PipeListener{ + addr: PipeAddr(address), + handle: handle, + }, nil +} + +// PipeListener is a named pipe listener. Clients should typically +// use variables of type net.Listener instead of assuming named pipe. +type PipeListener struct { + addr PipeAddr + handle syscall.Handle + closed bool + + // acceptHandle contains the current handle waiting for + // an incoming connection or nil. + acceptHandle syscall.Handle + // acceptOverlapped is set before waiting on a connection. + // If not waiting, it is nil. + acceptOverlapped *syscall.Overlapped + // acceptMutex protects the handle and overlapped structure. + acceptMutex sync.Mutex +} + +// Accept implements the Accept method in the net.Listener interface; it +// waits for the next call and returns a generic net.Conn. +func (l *PipeListener) Accept() (net.Conn, error) { + c, err := l.AcceptPipe() + for err == error_no_data { + // Ignore clients that connect and immediately disconnect. + c, err = l.AcceptPipe() + } + if err != nil { + return nil, err + } + return c, nil +} + +// AcceptPipe accepts the next incoming call and returns the new connection. +// It might return an error if a client connected and immediately cancelled +// the connection. +func (l *PipeListener) AcceptPipe() (*PipeConn, error) { + if l == nil || l.addr == "" || l.closed { + return nil, syscall.EINVAL + } + + // the first time we call accept, the handle will have been created by the Listen + // call. This is to prevent race conditions where the client thinks the server + // isn't listening because it hasn't actually called create yet. After the first time, we'll + // have to create a new handle each time + handle := l.handle + if handle == 0 { + var err error + handle, err = createPipe(string(l.addr), false) + if err != nil { + return nil, err + } + } else { + l.handle = 0 + } + + overlapped, err := newOverlapped() + if err != nil { + return nil, err + } + defer syscall.CloseHandle(overlapped.HEvent) + if err := connectNamedPipe(handle, overlapped); err != nil && err != error_pipe_connected { + if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING { + l.acceptMutex.Lock() + l.acceptOverlapped = overlapped + l.acceptHandle = handle + l.acceptMutex.Unlock() + defer func() { + l.acceptMutex.Lock() + l.acceptOverlapped = nil + l.acceptHandle = 0 + l.acceptMutex.Unlock() + }() + + _, err = waitForCompletion(handle, overlapped) + } + if err == syscall.ERROR_OPERATION_ABORTED { + // Return error compatible to net.Listener.Accept() in case the + // listener was closed. + return nil, ErrClosed + } + if err != nil { + return nil, err + } + } + return &PipeConn{handle: handle, addr: l.addr}, nil +} + +// Close stops listening on the address. +// Already Accepted connections are not closed. +func (l *PipeListener) Close() error { + if l.closed { + return nil + } + l.closed = true + if l.handle != 0 { + err := disconnectNamedPipe(l.handle) + if err != nil { + return err + } + err = syscall.CloseHandle(l.handle) + if err != nil { + return err + } + l.handle = 0 + } + l.acceptMutex.Lock() + defer l.acceptMutex.Unlock() + if l.acceptOverlapped != nil && l.acceptHandle != 0 { + // Cancel the pending IO. This call does not block, so it is safe + // to hold onto the mutex above. + if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil { + return err + } + err := syscall.CloseHandle(l.acceptOverlapped.HEvent) + if err != nil { + return err + } + l.acceptOverlapped.HEvent = 0 + err = syscall.CloseHandle(l.acceptHandle) + if err != nil { + return err + } + l.acceptHandle = 0 + } + return nil +} + +// Addr returns the listener's network address, a PipeAddr. +func (l *PipeListener) Addr() net.Addr { return l.addr } + +// PipeConn is the implementation of the net.Conn interface for named pipe connections. +type PipeConn struct { + handle syscall.Handle + addr PipeAddr + + // these aren't actually used yet + readDeadline *time.Time + writeDeadline *time.Time +} + +type iodata struct { + n uint32 + err error +} + +// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to +// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending, +// the content of iodata is returned. +func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { + if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { + var timer <-chan time.Time + if deadline != nil { + if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { + timer = time.After(timeDiff) + } + } + done := make(chan iodata) + go func() { + n, err := waitForCompletion(c.handle, overlapped) + done <- iodata{n, err} + }() + select { + case data = <-done: + case <-timer: + syscall.CancelIoEx(c.handle, overlapped) + data = iodata{0, timeout(c.addr.String())} + } + } + // Windows will produce ERROR_BROKEN_PIPE upon closing + // a handle on the other end of a connection. Go RPC + // expects an io.EOF error in this case. + if data.err == syscall.ERROR_BROKEN_PIPE { + data.err = io.EOF + } + return int(data.n), data.err +} + +// Read implements the net.Conn Read method. +func (c *PipeConn) Read(b []byte) (int, error) { + // Use ReadFile() rather than Read() because the latter + // contains a workaround that eats ERROR_BROKEN_PIPE. + overlapped, err := newOverlapped() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(overlapped.HEvent) + var n uint32 + err = syscall.ReadFile(c.handle, b, &n, overlapped) + return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped) +} + +// Write implements the net.Conn Write method. +func (c *PipeConn) Write(b []byte) (int, error) { + overlapped, err := newOverlapped() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(overlapped.HEvent) + var n uint32 + err = syscall.WriteFile(c.handle, b, &n, overlapped) + return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped) +} + +// Close closes the connection. +func (c *PipeConn) Close() error { + return syscall.CloseHandle(c.handle) +} + +// LocalAddr returns the local network address. +func (c *PipeConn) LocalAddr() net.Addr { + return c.addr +} + +// RemoteAddr returns the remote network address. +func (c *PipeConn) RemoteAddr() net.Addr { + // not sure what to do here, we don't have remote addr.... + return c.addr +} + +// SetDeadline implements the net.Conn SetDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetDeadline(t time.Time) error { + c.SetReadDeadline(t) + c.SetWriteDeadline(t) + return nil +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetReadDeadline(t time.Time) error { + c.readDeadline = &t + return nil +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetWriteDeadline(t time.Time) error { + c.writeDeadline = &t + return nil +} + +// PipeAddr represents the address of a named pipe. +type PipeAddr string + +// Network returns the address's network name, "pipe". +func (a PipeAddr) Network() string { return "pipe" } + +// String returns the address of the pipe +func (a PipeAddr) String() string { + return string(a) +} + +// createPipe is a helper function to make sure we always create pipes +// with the same arguments, since subsequent calls to create pipe need +// to use the same arguments as the first one. If first is set, fail +// if the pipe already exists. +func createPipe(address string, first bool) (syscall.Handle, error) { + n, err := syscall.UTF16PtrFromString(address) + if err != nil { + return 0, err + } + mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED) + if first { + mode |= file_flag_first_pipe_instance + } + return createNamedPipe(n, + mode, + pipe_type_byte, + pipe_unlimited_instances, + 512, 512, 0, nil) +} + +func badAddr(addr string) PipeError { + return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false} +} +func timeout(addr string) PipeError { + return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} +} + + + +func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { + c, err := Dial(cfg.Endpoint) + if err != nil { + return nil, err + } + + return &ipcClient{codec.New(c)}, nil +} + +func startIpc(cfg IpcConfig, codec codec.Codec, api api.Ethereum) error { + os.Remove(cfg.Endpoint) // in case it still exists from a previous run + + l, err := Listen(cfg.Endpoint) + if err != nil { + return err + } + os.Chmod(cfg.Endpoint, 0600) + + go func() { + for { + conn, err := l.Accept() + if err != nil { + glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err) + continue + } + + go func(conn net.Conn) { + codec := codec.New(conn) + + for { + req, err := codec.ReadRequest() + if err == io.EOF { + codec.Close() + return + } else if err != nil { + glog.V(logger.Error).Infof("IPC recv err - %v\n", err) + codec.Close() + return + } + + var rpcResponse interface{} + res, err := api.Execute(req) + + rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) + err = codec.WriteResponse(rpcResponse) + if err != nil { + glog.V(logger.Error).Infof("IPC send err - %v\n", err) + codec.Close() + return + } + } + }(conn) + } + }() + + glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) + + return nil +} diff --git a/rpc/shared/types.go b/rpc/shared/types.go index 46fd5552c..600d39541 100644 --- a/rpc/shared/types.go +++ b/rpc/shared/types.go @@ -1,6 +1,10 @@ package shared -import "encoding/json" +import ( + "encoding/json" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) // RPC request type Request struct { @@ -36,3 +40,24 @@ type ErrorObject struct { Message string `json:"message"` // Data interface{} `json:"data"` } + +func NewRpcResponse(id interface{}, jsonrpcver string, reply interface{}, err error) *interface{} { + var response interface{} + + switch err.(type) { + case nil: + response = &SuccessResponse{Jsonrpc: jsonrpcver, Id: id, Result: reply} + case *NotImplementedError: + jsonerr := &ErrorObject{-32601, err.Error()} + response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} + case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: + jsonerr := &ErrorObject{-32602, err.Error()} + response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} + default: + jsonerr := &ErrorObject{-32603, err.Error()} + response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} + } + + glog.V(logger.Detail).Infof("Generated response: %T %s", response, response) + return &response +} -- cgit v1.2.3 From 2a0d888326036be9cabe6680617ce2d1a27761d3 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 11:01:02 +0200 Subject: added API/IPC commandline flags --- rpc/api/api.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index 758e056ed..93dc3058c 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -2,6 +2,11 @@ package api import "github.com/ethereum/go-ethereum/rpc/shared" +const ( + // List with all API's which are offered over the IPC interface by default + DefaultIpcApis = "eth" +) + // Ethereum RPC API interface type EthereumApi interface { // Execute the given request and returns the response or an error -- cgit v1.2.3 From a1a475fb9296e214292840d89811123292c7953c Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 12:43:58 +0200 Subject: added console command --- rpc/api/api.go | 14 ++++++++- rpc/api/eth.go | 4 +++ rpc/api/mergedapi.go | 56 +++++++++++++++++++++++++++++++++ rpc/api/mergedapi_js.go | 1 + rpc/api/utils.go | 47 ++------------------------- rpc/api/web3.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/web3_args.go | 5 +++ rpc/jeth.go | 63 ++++++++++++++++++++++++++++--------- 8 files changed, 213 insertions(+), 61 deletions(-) create mode 100644 rpc/api/mergedapi.go create mode 100644 rpc/api/mergedapi_js.go create mode 100644 rpc/api/web3.go create mode 100644 rpc/api/web3_args.go (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index 93dc3058c..153c73f48 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -4,14 +4,26 @@ import "github.com/ethereum/go-ethereum/rpc/shared" const ( // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = "eth" + DefaultIpcApis = "eth,web3" + + EthApiName = "eth" + MergedApiName = "merged" + Web3ApiName = "web3" ) // Ethereum RPC API interface type EthereumApi interface { + // API identifier + Name() string + // Execute the given request and returns the response or an error Execute(*shared.Request) (interface{}, error) // List of supported RCP methods this API provides Methods() []string } + +// Merge multiple API's to a single API instance +func Merge(apis ...EthereumApi) EthereumApi { + return newMergedApi(apis...) +} diff --git a/rpc/api/eth.go b/rpc/api/eth.go index fa14aa41e..0a8cecdbc 100644 --- a/rpc/api/eth.go +++ b/rpc/api/eth.go @@ -93,6 +93,10 @@ func (self *EthApi) Execute(req *shared.Request) (interface{}, error) { return nil, shared.NewNotImplementedError(req.Method) } +func (self *EthApi) Name() string { + return EthApiName +} + func (self *EthApi) Accounts(req *shared.Request) (interface{}, error) { return self.xeth.Accounts(), nil } diff --git a/rpc/api/mergedapi.go b/rpc/api/mergedapi.go new file mode 100644 index 000000000..7784661d7 --- /dev/null +++ b/rpc/api/mergedapi.go @@ -0,0 +1,56 @@ +package api + +import "github.com/ethereum/go-ethereum/rpc/shared" + +// combines multiple API's +type mergedApi struct { + apis []string + methods map[string]EthereumApi +} + +// create new merged api instance +func newMergedApi(apis ...EthereumApi) *mergedApi { + mergedApi := new(mergedApi) + mergedApi.apis = make([]string, len(apis)) + mergedApi.methods = make(map[string]EthereumApi) + + for i, api := range apis { + mergedApi.apis[i] = api.Name() + for _, method := range api.Methods() { + mergedApi.methods[method] = api + } + } + return mergedApi +} + +// Supported RPC methods +func (self *mergedApi) Methods() []string { + all := make([]string, len(self.methods)) + for method, _ := range self.methods { + all = append(all, method) + } + return all +} + +// Call the correct API's Execute method for the given request +func (self *mergedApi) Execute(req *shared.Request) (interface{}, error) { + if res, _ := self.handle(req); res != nil { + return res, nil + } + if api, found := self.methods[req.Method]; found { + return api.Execute(req) + } + return nil, shared.NewNotImplementedError(req.Method) +} + +func (self *mergedApi) Name() string { + return MergedApiName +} + +func (self *mergedApi) handle(req *shared.Request) (interface{}, error) { + if req.Method == "support_apis" { // provided API's + return self.apis, nil + } + + return nil, nil +} diff --git a/rpc/api/mergedapi_js.go b/rpc/api/mergedapi_js.go new file mode 100644 index 000000000..778f64ec1 --- /dev/null +++ b/rpc/api/mergedapi_js.go @@ -0,0 +1 @@ +package api diff --git a/rpc/api/utils.go b/rpc/api/utils.go index a62058140..7024365e4 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -8,11 +8,6 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/xeth" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -const ( - EthApiName = "eth" ) // Parse a comma separated API string to individual api's @@ -28,6 +23,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. switch strings.ToLower(strings.TrimSpace(name)) { case EthApiName: apis[i] = NewEthApi(xeth, codec) + case Web3ApiName: + apis[i] = NewWeb3(xeth, codec) default: return nil, fmt.Errorf("Unknown API '%s'", name) } @@ -35,43 +32,3 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. return apis, nil } - -// combines multiple API's -type mergedApi struct { - apis map[string]EthereumApi -} - -// create new merged api instance -func newMergedApi(apis ...EthereumApi) *mergedApi { - mergedApi := new(mergedApi) - mergedApi.apis = make(map[string]EthereumApi) - - for _, api := range apis { - for _, method := range api.Methods() { - mergedApi.apis[method] = api - } - } - return mergedApi -} - -// Supported RPC methods -func (self *mergedApi) Methods() []string { - all := make([]string, len(self.apis)) - for method, _ := range self.apis { - all = append(all, method) - } - return all -} - -// Call the correct API's Execute method for the given request -func (self *mergedApi) Execute(req *shared.Request) (interface{}, error) { - if api, found := self.apis[req.Method]; found { - return api.Execute(req) - } - return nil, shared.NewNotImplementedError(req.Method) -} - -// Merge multiple API's to a single API instance -func Merge(apis ...EthereumApi) EthereumApi { - return newMergedApi(apis...) -} diff --git a/rpc/api/web3.go b/rpc/api/web3.go new file mode 100644 index 000000000..4c51c4a97 --- /dev/null +++ b/rpc/api/web3.go @@ -0,0 +1,84 @@ +package api + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +const ( + Web3Version = "1.0.0" +) + +var ( +// mapping between methods and handlers + Web3Mapping = map[string]web3handler{ + "web3_sha3": (*web3).Sha3, + "web3_clientVersion": (*web3).ClientVersion, + } +) + +// web3 callback handler +type web3handler func(*web3, *shared.Request) (interface{}, error) + +// web3 api provider +type web3 struct { + xeth *xeth.XEth + methods map[string]web3handler + codec codec.ApiCoder +} + +// create a new web3 api instance +func NewWeb3(xeth *xeth.XEth, coder codec.Codec) *web3 { + return &web3{ + xeth: xeth, + methods: Web3Mapping, + codec: coder.New(nil), + } +} + +// collection with supported methods +func (self *web3) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *web3) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, &shared.NotImplementedError{req.Method} +} + +func (self *web3) Name() string { + return Web3ApiName +} + +// Version of the API this instance provides +func (self *web3) Version() string { + return Web3Version +} + +// Calculates the sha3 over req.Params.Data +func (self *web3) Sha3(req *shared.Request) (interface{}, error) { + args := new(Sha3Args) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + + return common.ToHex(crypto.Sha3(common.FromHex(args.Data))), nil +} + +// returns the xeth client vrsion +func (self *web3) ClientVersion(req *shared.Request) (interface{}, error) { + return self.xeth.ClientVersion(), nil +} diff --git a/rpc/api/web3_args.go b/rpc/api/web3_args.go new file mode 100644 index 000000000..5455a6c8e --- /dev/null +++ b/rpc/api/web3_args.go @@ -0,0 +1,5 @@ +package api + +type Sha3Args struct { + Data string +} diff --git a/rpc/jeth.go b/rpc/jeth.go index 61be60dc7..d4f6dd460 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -6,15 +6,20 @@ import ( "github.com/ethereum/go-ethereum/jsre" "github.com/robertkrimen/otto" + "github.com/ethereum/go-ethereum/rpc/comms" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "reflect" ) type Jeth struct { - ethApi *EthereumApi - re *jsre.JSRE + ethApi *EthereumApi + re *jsre.JSRE + ipcpath string } -func NewJeth(ethApi *EthereumApi, re *jsre.JSRE) *Jeth { - return &Jeth{ethApi, re} +func NewJeth(ethApi *EthereumApi, re *jsre.JSRE, ipcpath string) *Jeth { + return &Jeth{ethApi, re, ipcpath} } func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) { @@ -34,6 +39,13 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { return self.err(call, -32700, err.Error(), nil) } + client, err := comms.NewIpcClient(comms.IpcConfig{self.ipcpath}, codec.JSON) + if err != nil { + fmt.Println("Unable to connect to geth.") + return self.err(call, -32603, err.Error(), -1) + } + defer client.Close() + jsonreq, err := json.Marshal(reqif) var reqs []RpcRequest batch := true @@ -48,22 +60,43 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { call.Otto.Run("var ret_response = new Array(response_len);") for i, req := range reqs { - var respif interface{} - err = self.ethApi.GetRequestReply(&req, &respif) + err := client.Send(&req) if err != nil { - fmt.Println("Error response:", err) + fmt.Println("Error send request:", err) return self.err(call, -32603, err.Error(), req.Id) } - call.Otto.Set("ret_jsonrpc", jsonrpcver) - call.Otto.Set("ret_id", req.Id) - res, _ := json.Marshal(respif) + respif, err := client.Recv() + if err != nil { + fmt.Println("Error recv response:", err) + return self.err(call, -32603, err.Error(), req.Id) + } - call.Otto.Set("ret_result", string(res)) - call.Otto.Set("response_idx", i) - response, err = call.Otto.Run(` - ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; - `) + if res, ok := respif.(shared.SuccessResponse); ok { + call.Otto.Set("ret_id", res.Id) + call.Otto.Set("ret_jsonrpc", res.Jsonrpc) + resObj, _ := json.Marshal(res.Result) + call.Otto.Set("ret_result", string(resObj)) + call.Otto.Set("response_idx", i) + + response, err = call.Otto.Run(` + ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; + `) + } else if res, ok := respif.(shared.ErrorResponse); ok { + fmt.Printf("Error: %s (%d)\n", res.Error.Message, res.Error.Code) + + call.Otto.Set("ret_id", res.Id) + call.Otto.Set("ret_jsonrpc", res.Jsonrpc) + call.Otto.Set("ret_error", res.Error) + call.Otto.Set("response_idx", i) + + response, _ = call.Otto.Run(` + ret_response = { jsonrpc: ret_jsonrpc, id: ret_id, error: ret_error }; + `) + return + } else { + fmt.Printf("unexpected response\n", reflect.TypeOf(respif)) + } } if !batch { -- cgit v1.2.3 From 862117e4bdcc5d255fc85fc35e223eec10f0ac7b Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 13:21:24 +0200 Subject: changed send methods for backwards compatability in geth console --- rpc/jeth.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'rpc') diff --git a/rpc/jeth.go b/rpc/jeth.go index d4f6dd460..0473adc4d 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -39,6 +39,60 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { return self.err(call, -32700, err.Error(), nil) } + jsonreq, err := json.Marshal(reqif) + var reqs []RpcRequest + batch := true + err = json.Unmarshal(jsonreq, &reqs) + if err != nil { + reqs = make([]RpcRequest, 1) + err = json.Unmarshal(jsonreq, &reqs[0]) + batch = false + } + + call.Otto.Set("response_len", len(reqs)) + call.Otto.Run("var ret_response = new Array(response_len);") + + for i, req := range reqs { + var respif interface{} + err = self.ethApi.GetRequestReply(&req, &respif) + 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_id", req.Id) + + res, _ := json.Marshal(respif) + + call.Otto.Set("ret_result", string(res)) + call.Otto.Set("response_idx", i) + response, err = call.Otto.Run(` + ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; + `) + } + + if !batch { + call.Otto.Run("ret_response = ret_response[0];") + } + + if call.Argument(1).IsObject() { + call.Otto.Set("callback", call.Argument(1)) + call.Otto.Run(` + if (Object.prototype.toString.call(callback) == '[object Function]') { + callback(null, ret_response); + } + `) + } + + return +} + +func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) { + reqif, err := call.Argument(0).Export() + if err != nil { + return self.err(call, -32700, err.Error(), nil) + } + client, err := comms.NewIpcClient(comms.IpcConfig{self.ipcpath}, codec.JSON) if err != nil { fmt.Println("Unable to connect to geth.") -- cgit v1.2.3 From 8aea85e374f3e95a899a923ce84423ddc11eb286 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 13:50:36 +0200 Subject: fixed windows build problem --- rpc/comms/ipc_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rpc') diff --git a/rpc/comms/ipc_windows.go b/rpc/comms/ipc_windows.go index ff9015d03..d989f0b2b 100644 --- a/rpc/comms/ipc_windows.go +++ b/rpc/comms/ipc_windows.go @@ -647,7 +647,7 @@ func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { return &ipcClient{codec.New(c)}, nil } -func startIpc(cfg IpcConfig, codec codec.Codec, api api.Ethereum) error { +func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { os.Remove(cfg.Endpoint) // in case it still exists from a previous run l, err := Listen(cfg.Endpoint) -- cgit v1.2.3 From 4b9b633dfe8c36d3a8909024ff23a1cdedce44d8 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 14:42:15 +0200 Subject: added miner API --- rpc/api/api.go | 3 +- rpc/api/miner.go | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/miner_args.go | 93 ++++++++++++++++++++++++++++++++ rpc/api/miner_js.go | 74 ++++++++++++++++++++++++++ rpc/api/utils.go | 11 ++++ 5 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 rpc/api/miner.go create mode 100644 rpc/api/miner_args.go create mode 100644 rpc/api/miner_js.go (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index 153c73f48..7b3774b4e 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -4,10 +4,11 @@ import "github.com/ethereum/go-ethereum/rpc/shared" const ( // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = "eth,web3" + DefaultIpcApis = "eth,web3,miner" EthApiName = "eth" MergedApiName = "merged" + MinerApiName = "miner" Web3ApiName = "web3" ) diff --git a/rpc/api/miner.go b/rpc/api/miner.go new file mode 100644 index 000000000..0e2ccf503 --- /dev/null +++ b/rpc/api/miner.go @@ -0,0 +1,143 @@ +package api + +import ( + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +const ( + MinerVersion = "1.0.0" +) + +var ( +// mapping between methods and handlers + MinerMapping = map[string]minerhandler{ + "miner_hashrate": (*miner).Hashrate, + "miner_makeDAG": (*miner).MakeDAG, + "miner_setExtra": (*miner).SetExtra, + "miner_setGasPrice": (*miner).SetGasPrice, + "miner_startAutoDAG": (*miner).StartAutoDAG, + "miner_start": (*miner).StartMiner, + "miner_stopAutoDAG": (*miner).StopAutoDAG, + "miner_stop": (*miner).StopMiner, + } +) + +// miner callback handler +type minerhandler func(*miner, *shared.Request) (interface{}, error) + +// miner api provider +type miner struct { + ethereum *eth.Ethereum + methods map[string]minerhandler + codec codec.ApiCoder +} + +// create a new miner api instance +func NewMinerApi(ethereum *eth.Ethereum, coder codec.Codec) *miner { + return &miner{ + ethereum: ethereum, + methods: MinerMapping, + codec: coder.New(nil), + } +} + +// Execute given request +func (self *miner) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, &shared.NotImplementedError{req.Method} +} + +// collection with supported methods +func (self *miner) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +func (self *miner) Name() string { + return MinerApiName +} + +func (self *miner) StartMiner(req *shared.Request) (interface{}, error) { + args := new(StartMinerArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + if args.Threads == -1 { // (not specified by user, use default) + args.Threads = self.ethereum.MinerThreads + } + + self.ethereum.StartAutoDAG() + err := self.ethereum.StartMining(args.Threads) + if err == nil { + return true, nil + } + + return false, err +} + +func (self *miner) StopMiner(req *shared.Request) (interface{}, error) { + self.ethereum.StopMining() + return true, nil +} + +func (self *miner) Hashrate(req *shared.Request) (interface{}, error) { + return self.ethereum.Miner().HashRate(), nil +} + +func (self *miner) SetExtra(req *shared.Request) (interface{}, error) { + args := new(SetExtraArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + self.ethereum.Miner().SetExtra([]byte(args.Data)) + return true, nil +} + +func (self *miner) SetGasPrice(req *shared.Request) (interface{}, error) { + args := new(GasPriceArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return false, err + } + + self.ethereum.Miner().SetGasPrice(common.String2Big(args.Price)) + return true, nil +} + +func (self *miner) StartAutoDAG(req *shared.Request) (interface{}, error) { + self.ethereum.StartAutoDAG() + return true, nil +} + +func (self *miner) StopAutoDAG(req *shared.Request) (interface{}, error) { + self.ethereum.StopAutoDAG() + return true, nil +} + +func (self *miner) MakeDAG(req *shared.Request) (interface{}, error) { + args := new(MakeDAGArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + + if args.BlockNumber < 0 { + return false, shared.NewValidationError("BlockNumber", "BlockNumber must be positive") + } + + err := ethash.MakeDAG(uint64(args.BlockNumber), "") + if err == nil { + return true, nil + } + return false, err +} \ No newline at end of file diff --git a/rpc/api/miner_args.go b/rpc/api/miner_args.go new file mode 100644 index 000000000..8b9114940 --- /dev/null +++ b/rpc/api/miner_args.go @@ -0,0 +1,93 @@ +package api + +import ( + "encoding/json" + + "math/big" + + "github.com/ethereum/go-ethereum/rpc/shared" +) + +type StartMinerArgs struct { + Threads int +} + +func (args *StartMinerArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) == 0 || obj[0] == nil { + args.Threads = -1 + return nil + } + + var num *big.Int + if num, err = numString(obj[0]); err != nil { + return err + } + args.Threads = int(num.Int64()) + return nil +} + +type SetExtraArgs struct { + Data string +} + +func (args *SetExtraArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + extrastr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("Price", "not a string") + } + args.Data = extrastr + + return nil +} + +type GasPriceArgs struct { + Price string +} + +func (args *GasPriceArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + pricestr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("Price", "not a string") + } + args.Price = pricestr + + return nil +} + +type MakeDAGArgs struct { + BlockNumber int64 +} + +func (args *MakeDAGArgs) UnmarshalJSON(b []byte) (err error) { + args.BlockNumber = -1 + var obj []interface{} + + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + + if err := blockHeight(obj[0], &args.BlockNumber); err != nil { + return err + } + + return nil +} \ No newline at end of file diff --git a/rpc/api/miner_js.go b/rpc/api/miner_js.go new file mode 100644 index 000000000..40fa3bc3d --- /dev/null +++ b/rpc/api/miner_js.go @@ -0,0 +1,74 @@ +package api + +const Miner_JS = ` +web3.extend({ + property: 'miner', + methods: + [ + new web3.extend.Method({ + name: 'start', + call: 'miner_start', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'stop', + call: 'miner_stop', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'getHashrate', + call: 'miner_hashrate', + params: 0, + inputFormatter: [], + outputFormatter: web3.extend.utils.toDecimal + }), + new web3.extend.Method({ + name: 'setExtra', + call: 'miner_setExtra', + params: 1, + inputFormatter: [web3.extend.utils.formatInputString], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'setGasPrice', + call: 'miner_setGasPrice', + params: 1, + inputFormatter: [web3.extend.utils.formatInputString], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'startAutoDAG', + call: 'miner_startAutoDAG', + params: 0, + inputFormatter: [], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'stopAutoDAG', + call: 'miner_stopAutoDAG', + params: 0, + inputFormatter: [], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'makeDAG', + call: 'miner_makeDAG', + params: 1, + inputFormatter: [web3.extend.formatters.inputDefaultBlockNumberFormatter], + outputFormatter: web3.extend.formatters.formatOutputBool + }) + ], + properties: + [ + new web3.extend.Property({ + name: 'hashrate', + getter: 'miner_hashrate', + outputFormatter: web3.extend.utils.toDecimal + }) + ] +}); +` \ No newline at end of file diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 7024365e4..488eb1ec6 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -23,6 +23,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. switch strings.ToLower(strings.TrimSpace(name)) { case EthApiName: apis[i] = NewEthApi(xeth, codec) + case MinerApiName: + apis[i] = NewMinerApi(eth, codec) case Web3ApiName: apis[i] = NewWeb3(xeth, codec) default: @@ -32,3 +34,12 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. return apis, nil } + +func Javascript(name string) string { + switch strings.ToLower(strings.TrimSpace(name)) { + case MinerApiName: + return Miner_JS + } + + return "" +} -- cgit v1.2.3 From d2a87f6f72b1582fd6e220e2a00d7c3f5a6df335 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 14:50:11 +0200 Subject: added net API --- rpc/api/api.go | 9 +++--- rpc/api/mergedapi.go | 4 +-- rpc/api/miner.go | 4 +-- rpc/api/miner_args.go | 2 +- rpc/api/miner_js.go | 2 +- rpc/api/net.go | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/net_js.go | 44 ++++++++++++++++++++++++++++ rpc/api/utils.go | 4 +++ rpc/api/web3.go | 2 +- 9 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 rpc/api/net.go create mode 100644 rpc/api/net_js.go (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index 7b3774b4e..e4f0e7446 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -4,12 +4,13 @@ import "github.com/ethereum/go-ethereum/rpc/shared" const ( // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = "eth,web3,miner" + DefaultIpcApis = "eth,miner,net,web3" - EthApiName = "eth" + EthApiName = "eth" MergedApiName = "merged" - MinerApiName = "miner" - Web3ApiName = "web3" + MinerApiName = "miner" + NetApiName = "net" + Web3ApiName = "web3" ) // Ethereum RPC API interface diff --git a/rpc/api/mergedapi.go b/rpc/api/mergedapi.go index 7784661d7..88c301aae 100644 --- a/rpc/api/mergedapi.go +++ b/rpc/api/mergedapi.go @@ -4,7 +4,7 @@ import "github.com/ethereum/go-ethereum/rpc/shared" // combines multiple API's type mergedApi struct { - apis []string + apis []string methods map[string]EthereumApi } @@ -48,7 +48,7 @@ func (self *mergedApi) Name() string { } func (self *mergedApi) handle(req *shared.Request) (interface{}, error) { - if req.Method == "support_apis" { // provided API's + if req.Method == "support_apis" { // provided API's return self.apis, nil } diff --git a/rpc/api/miner.go b/rpc/api/miner.go index 0e2ccf503..b22c4b7ad 100644 --- a/rpc/api/miner.go +++ b/rpc/api/miner.go @@ -13,7 +13,7 @@ const ( ) var ( -// mapping between methods and handlers + // mapping between methods and handlers MinerMapping = map[string]minerhandler{ "miner_hashrate": (*miner).Hashrate, "miner_makeDAG": (*miner).MakeDAG, @@ -140,4 +140,4 @@ func (self *miner) MakeDAG(req *shared.Request) (interface{}, error) { return true, nil } return false, err -} \ No newline at end of file +} diff --git a/rpc/api/miner_args.go b/rpc/api/miner_args.go index 8b9114940..6b3d16d48 100644 --- a/rpc/api/miner_args.go +++ b/rpc/api/miner_args.go @@ -90,4 +90,4 @@ func (args *MakeDAGArgs) UnmarshalJSON(b []byte) (err error) { } return nil -} \ No newline at end of file +} diff --git a/rpc/api/miner_js.go b/rpc/api/miner_js.go index 40fa3bc3d..f1c64c5e8 100644 --- a/rpc/api/miner_js.go +++ b/rpc/api/miner_js.go @@ -71,4 +71,4 @@ web3.extend({ }) ] }); -` \ No newline at end of file +` diff --git a/rpc/api/net.go b/rpc/api/net.go new file mode 100644 index 000000000..6799d68f6 --- /dev/null +++ b/rpc/api/net.go @@ -0,0 +1,81 @@ +package api + +import ( + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +var ( + // mapping between methods and handlers + netMapping = map[string]nethandler{ + "net_id": (*net).NetworkVersion, + "net_peerCount": (*net).PeerCount, + "net_listening": (*net).IsListening, + "net_peers": (*net).Peers, + } +) + +// net callback handler +type nethandler func(*net, *shared.Request) (interface{}, error) + +// net api provider +type net struct { + xeth *xeth.XEth + ethereum *eth.Ethereum + methods map[string]nethandler + codec codec.ApiCoder +} + +// create a new net api instance +func NewNetApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *net { + return &net{ + xeth: xeth, + ethereum: eth, + methods: netMapping, + codec: coder.New(nil), + } +} + +// collection with supported methods +func (self *net) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *net) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, shared.NewNotImplementedError(req.Method) +} + +func (self *net) Name() string { + return NetApiName +} + +// Network version +func (self *net) NetworkVersion(req *shared.Request) (interface{}, error) { + return self.xeth.NetworkVersion(), nil +} + +// Number of connected peers +func (self *net) PeerCount(req *shared.Request) (interface{}, error) { + return self.xeth.PeerCount(), nil +} + +func (self *net) IsListening(req *shared.Request) (interface{}, error) { + return self.xeth.IsListening(), nil +} + +func (self *net) Peers(req *shared.Request) (interface{}, error) { + return self.ethereum.PeersInfo(), nil +} diff --git a/rpc/api/net_js.go b/rpc/api/net_js.go new file mode 100644 index 000000000..6ba0624d8 --- /dev/null +++ b/rpc/api/net_js.go @@ -0,0 +1,44 @@ +package api + +const Net_JS = ` +web3.extend({ + property: 'network', + methods: + [ + new web3.extend.Method({ + name: 'id', + call: 'net_id', + params: 0, + inputFormatter: [], + outputFormatter: web3.extend.formatters.formatOutputString + }), + new web3.extend.Method({ + name: 'getPeerCount', + call: 'net_peerCount', + params: 0, + inputFormatter: [], + outputFormatter: web3.extend.formatters.formatOutputString + }), + new web3.extend.Method({ + name: 'peers', + call: 'net_peers', + params: 0, + inputFormatter: [], + outputFormatter: function(obj) { return obj; } + }) + ], + properties: + [ + new web3.extend.Property({ + name: 'listening', + getter: 'net_listening', + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Property({ + name: 'peerCount', + getter: 'net_peerCount', + outputFormatter: web3.extend.utils.toDecimal + }) + ] +}); +` diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 488eb1ec6..173a880d4 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -25,6 +25,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. apis[i] = NewEthApi(xeth, codec) case MinerApiName: apis[i] = NewMinerApi(eth, codec) + case NetApiName: + apis[i] = NewNetApi(xeth, eth, codec) case Web3ApiName: apis[i] = NewWeb3(xeth, codec) default: @@ -39,6 +41,8 @@ func Javascript(name string) string { switch strings.ToLower(strings.TrimSpace(name)) { case MinerApiName: return Miner_JS + case NetApiName: + return Net_JS } return "" diff --git a/rpc/api/web3.go b/rpc/api/web3.go index 4c51c4a97..c46457ce6 100644 --- a/rpc/api/web3.go +++ b/rpc/api/web3.go @@ -13,7 +13,7 @@ const ( ) var ( -// mapping between methods and handlers + // mapping between methods and handlers Web3Mapping = map[string]web3handler{ "web3_sha3": (*web3).Sha3, "web3_clientVersion": (*web3).ClientVersion, -- cgit v1.2.3 From 09d0d55fc579701191ff34f38cc20b437ee23577 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Tue, 9 Jun 2015 09:48:18 +0200 Subject: added debug API --- rpc/api/api.go | 3 +- rpc/api/debug.go | 169 ++++++ rpc/api/debug_args.go | 47 ++ rpc/api/debug_js.go | 48 ++ rpc/api/utils.go | 4 + rpc/comms/ipc_unix.go | 10 +- rpc/comms/ipc_windows.go | 1395 +++++++++++++++++++++++----------------------- rpc/jeth.go | 7 +- rpc/shared/types.go | 9 +- 9 files changed, 980 insertions(+), 712 deletions(-) create mode 100644 rpc/api/debug.go create mode 100644 rpc/api/debug_args.go create mode 100644 rpc/api/debug_js.go (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index e4f0e7446..067a4d4e8 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -4,9 +4,10 @@ import "github.com/ethereum/go-ethereum/rpc/shared" const ( // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = "eth,miner,net,web3" + DefaultIpcApis = "debug,eth,miner,net,web3" EthApiName = "eth" + DebugApiName = "debug" MergedApiName = "merged" MinerApiName = "miner" NetApiName = "net" diff --git a/rpc/api/debug.go b/rpc/api/debug.go new file mode 100644 index 000000000..26f43fe74 --- /dev/null +++ b/rpc/api/debug.go @@ -0,0 +1,169 @@ +package api + +import ( + "fmt" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +const ( + DebugVersion = "1.0.0" +) + +var ( + // mapping between methods and handlers + DebugMapping = map[string]debughandler{ + "debug_dumpBlock": (*DebugApi).DumpBlock, + "debug_getBlockRlp": (*DebugApi).GetBlockRlp, + "debug_printBlock": (*DebugApi).PrintBlock, + "debug_processBlock": (*DebugApi).ProcessBlock, + "debug_seedHash": (*DebugApi).SeedHash, + "debug_setHead": (*DebugApi).SetHead, + } +) + +// debug callback handler +type debughandler func(*DebugApi, *shared.Request) (interface{}, error) + +// admin api provider +type DebugApi struct { + xeth *xeth.XEth + ethereum *eth.Ethereum + methods map[string]debughandler + codec codec.ApiCoder +} + +// create a new debug api instance +func NewDebugApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *DebugApi { + return &DebugApi{ + xeth: xeth, + ethereum: ethereum, + methods: DebugMapping, + codec: coder.New(nil), + } +} + +// collection with supported methods +func (self *DebugApi) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *DebugApi) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, &shared.NotImplementedError{req.Method} +} + +func (self *DebugApi) Name() string { + return DebugApiName +} + +func (self *DebugApi) PrintBlock(req *shared.Request) (interface{}, error) { + args := new(BlockNumArg) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByNumber(args.BlockNumber) + return fmt.Sprintf("%s", block), nil +} + +func (self *DebugApi) DumpBlock(req *shared.Request) (interface{}, error) { + args := new(BlockNumArg) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByNumber(args.BlockNumber) + if block == nil { + return nil, fmt.Errorf("block #%d not found", args.BlockNumber) + } + + stateDb := state.New(block.Root(), self.ethereum.StateDb()) + if stateDb == nil { + return nil, nil + } + + return stateDb.Dump(), nil +} + +func (self *DebugApi) GetBlockRlp(req *shared.Request) (interface{}, error) { + args := new(BlockNumArg) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByNumber(args.BlockNumber) + if block == nil { + return nil, fmt.Errorf("block #%d not found", args.BlockNumber) + } + encoded, err := rlp.EncodeToBytes(block) + return fmt.Sprintf("%x", encoded), err +} + +func (self *DebugApi) SetHead(req *shared.Request) (interface{}, error) { + args := new(BlockNumArg) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByNumber(args.BlockNumber) + if block == nil { + return nil, fmt.Errorf("block #%d not found", args.BlockNumber) + } + + self.ethereum.ChainManager().SetHead(block) + + return nil, nil +} + +func (self *DebugApi) ProcessBlock(req *shared.Request) (interface{}, error) { + args := new(BlockNumArg) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + block := self.xeth.EthBlockByNumber(args.BlockNumber) + if block == nil { + return nil, fmt.Errorf("block #%d not found", args.BlockNumber) + } + + old := vm.Debug + defer func() { vm.Debug = old }() + vm.Debug = true + + _, err := self.ethereum.BlockProcessor().RetryProcess(block) + if err == nil { + return true, nil + } + return false, err +} + +func (self *DebugApi) SeedHash(req *shared.Request) (interface{}, error) { + args := new(BlockNumArg) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + if hash, err := ethash.GetSeedHash(uint64(args.BlockNumber)); err == nil { + return fmt.Sprintf("0x%x", hash), nil + } else { + return nil, err + } +} diff --git a/rpc/api/debug_args.go b/rpc/api/debug_args.go new file mode 100644 index 000000000..b9b5aa27e --- /dev/null +++ b/rpc/api/debug_args.go @@ -0,0 +1,47 @@ +package api + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/rpc/shared" +) + +type WaitForBlockArgs struct { + MinHeight int + Timeout int // in seconds +} + +func (args *WaitForBlockArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + if len(obj) > 2 { + return fmt.Errorf("waitForArgs needs 0, 1, 2 arguments") + } + + // default values when not provided + args.MinHeight = -1 + args.Timeout = -1 + + if len(obj) >= 1 { + var minHeight *big.Int + if minHeight, err = numString(obj[0]); err != nil { + return err + } + args.MinHeight = int(minHeight.Int64()) + } + + if len(obj) >= 2 { + timeout, err := numString(obj[1]) + if err != nil { + return err + } + args.Timeout = int(timeout.Int64()) + } + + return nil +} diff --git a/rpc/api/debug_js.go b/rpc/api/debug_js.go new file mode 100644 index 000000000..43c545b2a --- /dev/null +++ b/rpc/api/debug_js.go @@ -0,0 +1,48 @@ +package api + +const Debug_JS = ` +web3.extend({ + property: 'debug', + methods: + [ + new web3.extend.Method({ + name: 'printBlock', + call: 'debug_printBlock', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputString + }), + new web3.extend.Method({ + name: 'getBlockRlp', + call: 'debug_getBlockRlp', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputString + }), + new web3.extend.Method({ + name: 'setHead', + call: 'debug_setHead', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'processBlock', + call: 'debug_processBlock', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputInt], + outputFormatter: function(obj) { return obj; } + }), + new web3.extend.Method({ + name: 'seedHash', + call: 'debug_seedHash', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputString + }) + ], + properties: + [ + ] +}); +` diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 173a880d4..6e6d5c7b0 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -21,6 +21,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. for i, name := range names { switch strings.ToLower(strings.TrimSpace(name)) { + case DebugApiName: + apis[i] = NewDebugApi(xeth, eth, codec) case EthApiName: apis[i] = NewEthApi(xeth, codec) case MinerApiName: @@ -39,6 +41,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. func Javascript(name string) string { switch strings.ToLower(strings.TrimSpace(name)) { + case DebugApiName: + return Debug_JS case MinerApiName: return Miner_JS case NetApiName: diff --git a/rpc/comms/ipc_unix.go b/rpc/comms/ipc_unix.go index bb09d9547..5a94fd1e0 100644 --- a/rpc/comms/ipc_unix.go +++ b/rpc/comms/ipc_unix.go @@ -3,9 +3,9 @@ package comms import ( - "io" + "io" "net" - "os" + "os" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -70,8 +70,8 @@ func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { os.Remove(cfg.Endpoint) }() - - glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) - + + glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) + return nil } diff --git a/rpc/comms/ipc_windows.go b/rpc/comms/ipc_windows.go index d989f0b2b..c48dfb7fb 100644 --- a/rpc/comms/ipc_windows.go +++ b/rpc/comms/ipc_windows.go @@ -1,699 +1,696 @@ -// +build windows - -package comms - -import ( - "fmt" - "io" - "net" - "os" - "sync" - "syscall" - "time" - "unsafe" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rpc/api" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -var ( - modkernel32 = syscall.NewLazyDLL("kernel32.dll") - - procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") - procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") - procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") - procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") - procCancelIoEx = modkernel32.NewProc("CancelIoEx") -) - -func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func disconnectNamedPipe(handle syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func waitNamedPipe(name *uint16, timeout uint32) (err error) { - r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { - var _p0 uint32 - if manualReset { - _p0 = 1 - } else { - _p0 = 0 - } - var _p1 uint32 - if initialState { - _p1 = 1 - } else { - _p1 = 0 - } - r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { - var _p0 uint32 - if wait { - _p0 = 1 - } else { - _p0 = 0 - } - r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - - -const ( - // openMode - pipe_access_duplex = 0x3 - pipe_access_inbound = 0x1 - pipe_access_outbound = 0x2 - - // openMode write flags - file_flag_first_pipe_instance = 0x00080000 - file_flag_write_through = 0x80000000 - file_flag_overlapped = 0x40000000 - - // openMode ACL flags - write_dac = 0x00040000 - write_owner = 0x00080000 - access_system_security = 0x01000000 - - // pipeMode - pipe_type_byte = 0x0 - pipe_type_message = 0x4 - - // pipeMode read mode flags - pipe_readmode_byte = 0x0 - pipe_readmode_message = 0x2 - - // pipeMode wait mode flags - pipe_wait = 0x0 - pipe_nowait = 0x1 - - // pipeMode remote-client mode flags - pipe_accept_remote_clients = 0x0 - pipe_reject_remote_clients = 0x8 - - pipe_unlimited_instances = 255 - - nmpwait_wait_forever = 0xFFFFFFFF - - // the two not-an-errors below occur if a client connects to the pipe between - // the server's CreateNamedPipe and ConnectNamedPipe calls. - error_no_data syscall.Errno = 0xE8 - error_pipe_connected syscall.Errno = 0x217 - error_pipe_busy syscall.Errno = 0xE7 - error_sem_timeout syscall.Errno = 0x79 - - error_bad_pathname syscall.Errno = 0xA1 - error_invalid_name syscall.Errno = 0x7B - - error_io_incomplete syscall.Errno = 0x3e4 -) - -var _ net.Conn = (*PipeConn)(nil) -var _ net.Listener = (*PipeListener)(nil) - -// ErrClosed is the error returned by PipeListener.Accept when Close is called -// on the PipeListener. -var ErrClosed = PipeError{"Pipe has been closed.", false} - -// PipeError is an error related to a call to a pipe -type PipeError struct { - msg string - timeout bool -} - -// Error implements the error interface -func (e PipeError) Error() string { - return e.msg -} - -// Timeout implements net.AddrError.Timeout() -func (e PipeError) Timeout() bool { - return e.timeout -} - -// Temporary implements net.AddrError.Temporary() -func (e PipeError) Temporary() bool { - return false -} - -// Dial connects to a named pipe with the given address. If the specified pipe is not available, -// it will wait indefinitely for the pipe to become available. -// -// The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ -// for remote pipes. -// -// Dial will return a PipeError if you pass in a badly formatted pipe name. -// -// Examples: -// // local pipe -// conn, err := Dial(`\\.\pipe\mypipename`) -// -// // remote pipe -// conn, err := Dial(`\\othercomp\pipe\mypipename`) -func Dial(address string) (*PipeConn, error) { - for { - conn, err := dial(address, nmpwait_wait_forever) - if err == nil { - return conn, nil - } - if isPipeNotReady(err) { - <-time.After(100 * time.Millisecond) - continue - } - return nil, err - } -} - -// DialTimeout acts like Dial, but will time out after the duration of timeout -func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) { - deadline := time.Now().Add(timeout) - - now := time.Now() - for now.Before(deadline) { - millis := uint32(deadline.Sub(now) / time.Millisecond) - conn, err := dial(address, millis) - if err == nil { - return conn, nil - } - if err == error_sem_timeout { - // This is WaitNamedPipe's timeout error, so we know we're done - return nil, PipeError{fmt.Sprintf( - "Timed out waiting for pipe '%s' to come available", address), true} - } - if isPipeNotReady(err) { - left := deadline.Sub(time.Now()) - retry := 100 * time.Millisecond - if left > retry { - <-time.After(retry) - } else { - <-time.After(left - time.Millisecond) - } - now = time.Now() - continue - } - return nil, err - } - return nil, PipeError{fmt.Sprintf( - "Timed out waiting for pipe '%s' to come available", address), true} -} - -// isPipeNotReady checks the error to see if it indicates the pipe is not ready -func isPipeNotReady(err error) bool { - // Pipe Busy means another client just grabbed the open pipe end, - // and the server hasn't made a new one yet. - // File Not Found means the server hasn't created the pipe yet. - // Neither is a fatal error. - - return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy -} - -// newOverlapped creates a structure used to track asynchronous -// I/O requests that have been issued. -func newOverlapped() (*syscall.Overlapped, error) { - event, err := createEvent(nil, true, true, nil) - if err != nil { - return nil, err - } - return &syscall.Overlapped{HEvent: event}, nil -} - -// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete. -// This function returns the number of bytes transferred by the operation and an error code if -// applicable (nil otherwise). -func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) { - _, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE) - if err != nil { - return 0, err - } - var transferred uint32 - err = getOverlappedResult(handle, overlapped, &transferred, true) - return transferred, err -} - -// dial is a helper to initiate a connection to a named pipe that has been started by a server. -// The timeout is only enforced if the pipe server has already created the pipe, otherwise -// this function will return immediately. -func dial(address string, timeout uint32) (*PipeConn, error) { - name, err := syscall.UTF16PtrFromString(string(address)) - if err != nil { - return nil, err - } - // If at least one instance of the pipe has been created, this function - // will wait timeout milliseconds for it to become available. - // It will return immediately regardless of timeout, if no instances - // of the named pipe have been created yet. - // If this returns with no error, there is a pipe available. - if err := waitNamedPipe(name, timeout); err != nil { - if err == error_bad_pathname { - // badly formatted pipe name - return nil, badAddr(address) - } - return nil, err - } - pathp, err := syscall.UTF16PtrFromString(address) - if err != nil { - return nil, err - } - handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, - uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, - syscall.FILE_FLAG_OVERLAPPED, 0) - if err != nil { - return nil, err - } - return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil -} - -// Listen returns a new PipeListener that will listen on a pipe with the given -// address. The address must be of the form \\.\pipe\ -// -// Listen will return a PipeError for an incorrectly formatted pipe name. -func Listen(address string) (*PipeListener, error) { - handle, err := createPipe(address, true) - if err == error_invalid_name { - return nil, badAddr(address) - } - if err != nil { - return nil, err - } - return &PipeListener{ - addr: PipeAddr(address), - handle: handle, - }, nil -} - -// PipeListener is a named pipe listener. Clients should typically -// use variables of type net.Listener instead of assuming named pipe. -type PipeListener struct { - addr PipeAddr - handle syscall.Handle - closed bool - - // acceptHandle contains the current handle waiting for - // an incoming connection or nil. - acceptHandle syscall.Handle - // acceptOverlapped is set before waiting on a connection. - // If not waiting, it is nil. - acceptOverlapped *syscall.Overlapped - // acceptMutex protects the handle and overlapped structure. - acceptMutex sync.Mutex -} - -// Accept implements the Accept method in the net.Listener interface; it -// waits for the next call and returns a generic net.Conn. -func (l *PipeListener) Accept() (net.Conn, error) { - c, err := l.AcceptPipe() - for err == error_no_data { - // Ignore clients that connect and immediately disconnect. - c, err = l.AcceptPipe() - } - if err != nil { - return nil, err - } - return c, nil -} - -// AcceptPipe accepts the next incoming call and returns the new connection. -// It might return an error if a client connected and immediately cancelled -// the connection. -func (l *PipeListener) AcceptPipe() (*PipeConn, error) { - if l == nil || l.addr == "" || l.closed { - return nil, syscall.EINVAL - } - - // the first time we call accept, the handle will have been created by the Listen - // call. This is to prevent race conditions where the client thinks the server - // isn't listening because it hasn't actually called create yet. After the first time, we'll - // have to create a new handle each time - handle := l.handle - if handle == 0 { - var err error - handle, err = createPipe(string(l.addr), false) - if err != nil { - return nil, err - } - } else { - l.handle = 0 - } - - overlapped, err := newOverlapped() - if err != nil { - return nil, err - } - defer syscall.CloseHandle(overlapped.HEvent) - if err := connectNamedPipe(handle, overlapped); err != nil && err != error_pipe_connected { - if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING { - l.acceptMutex.Lock() - l.acceptOverlapped = overlapped - l.acceptHandle = handle - l.acceptMutex.Unlock() - defer func() { - l.acceptMutex.Lock() - l.acceptOverlapped = nil - l.acceptHandle = 0 - l.acceptMutex.Unlock() - }() - - _, err = waitForCompletion(handle, overlapped) - } - if err == syscall.ERROR_OPERATION_ABORTED { - // Return error compatible to net.Listener.Accept() in case the - // listener was closed. - return nil, ErrClosed - } - if err != nil { - return nil, err - } - } - return &PipeConn{handle: handle, addr: l.addr}, nil -} - -// Close stops listening on the address. -// Already Accepted connections are not closed. -func (l *PipeListener) Close() error { - if l.closed { - return nil - } - l.closed = true - if l.handle != 0 { - err := disconnectNamedPipe(l.handle) - if err != nil { - return err - } - err = syscall.CloseHandle(l.handle) - if err != nil { - return err - } - l.handle = 0 - } - l.acceptMutex.Lock() - defer l.acceptMutex.Unlock() - if l.acceptOverlapped != nil && l.acceptHandle != 0 { - // Cancel the pending IO. This call does not block, so it is safe - // to hold onto the mutex above. - if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil { - return err - } - err := syscall.CloseHandle(l.acceptOverlapped.HEvent) - if err != nil { - return err - } - l.acceptOverlapped.HEvent = 0 - err = syscall.CloseHandle(l.acceptHandle) - if err != nil { - return err - } - l.acceptHandle = 0 - } - return nil -} - -// Addr returns the listener's network address, a PipeAddr. -func (l *PipeListener) Addr() net.Addr { return l.addr } - -// PipeConn is the implementation of the net.Conn interface for named pipe connections. -type PipeConn struct { - handle syscall.Handle - addr PipeAddr - - // these aren't actually used yet - readDeadline *time.Time - writeDeadline *time.Time -} - -type iodata struct { - n uint32 - err error -} - -// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to -// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending, -// the content of iodata is returned. -func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { - if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { - var timer <-chan time.Time - if deadline != nil { - if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { - timer = time.After(timeDiff) - } - } - done := make(chan iodata) - go func() { - n, err := waitForCompletion(c.handle, overlapped) - done <- iodata{n, err} - }() - select { - case data = <-done: - case <-timer: - syscall.CancelIoEx(c.handle, overlapped) - data = iodata{0, timeout(c.addr.String())} - } - } - // Windows will produce ERROR_BROKEN_PIPE upon closing - // a handle on the other end of a connection. Go RPC - // expects an io.EOF error in this case. - if data.err == syscall.ERROR_BROKEN_PIPE { - data.err = io.EOF - } - return int(data.n), data.err -} - -// Read implements the net.Conn Read method. -func (c *PipeConn) Read(b []byte) (int, error) { - // Use ReadFile() rather than Read() because the latter - // contains a workaround that eats ERROR_BROKEN_PIPE. - overlapped, err := newOverlapped() - if err != nil { - return 0, err - } - defer syscall.CloseHandle(overlapped.HEvent) - var n uint32 - err = syscall.ReadFile(c.handle, b, &n, overlapped) - return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped) -} - -// Write implements the net.Conn Write method. -func (c *PipeConn) Write(b []byte) (int, error) { - overlapped, err := newOverlapped() - if err != nil { - return 0, err - } - defer syscall.CloseHandle(overlapped.HEvent) - var n uint32 - err = syscall.WriteFile(c.handle, b, &n, overlapped) - return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped) -} - -// Close closes the connection. -func (c *PipeConn) Close() error { - return syscall.CloseHandle(c.handle) -} - -// LocalAddr returns the local network address. -func (c *PipeConn) LocalAddr() net.Addr { - return c.addr -} - -// RemoteAddr returns the remote network address. -func (c *PipeConn) RemoteAddr() net.Addr { - // not sure what to do here, we don't have remote addr.... - return c.addr -} - -// SetDeadline implements the net.Conn SetDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetDeadline(t time.Time) error { - c.SetReadDeadline(t) - c.SetWriteDeadline(t) - return nil -} - -// SetReadDeadline implements the net.Conn SetReadDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetReadDeadline(t time.Time) error { - c.readDeadline = &t - return nil -} - -// SetWriteDeadline implements the net.Conn SetWriteDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetWriteDeadline(t time.Time) error { - c.writeDeadline = &t - return nil -} - -// PipeAddr represents the address of a named pipe. -type PipeAddr string - -// Network returns the address's network name, "pipe". -func (a PipeAddr) Network() string { return "pipe" } - -// String returns the address of the pipe -func (a PipeAddr) String() string { - return string(a) -} - -// createPipe is a helper function to make sure we always create pipes -// with the same arguments, since subsequent calls to create pipe need -// to use the same arguments as the first one. If first is set, fail -// if the pipe already exists. -func createPipe(address string, first bool) (syscall.Handle, error) { - n, err := syscall.UTF16PtrFromString(address) - if err != nil { - return 0, err - } - mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED) - if first { - mode |= file_flag_first_pipe_instance - } - return createNamedPipe(n, - mode, - pipe_type_byte, - pipe_unlimited_instances, - 512, 512, 0, nil) -} - -func badAddr(addr string) PipeError { - return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false} -} -func timeout(addr string) PipeError { - return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} -} - - - -func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { - c, err := Dial(cfg.Endpoint) - if err != nil { - return nil, err - } - - return &ipcClient{codec.New(c)}, nil -} - -func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { - os.Remove(cfg.Endpoint) // in case it still exists from a previous run - - l, err := Listen(cfg.Endpoint) - if err != nil { - return err - } - os.Chmod(cfg.Endpoint, 0600) - - go func() { - for { - conn, err := l.Accept() - if err != nil { - glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err) - continue - } - - go func(conn net.Conn) { - codec := codec.New(conn) - - for { - req, err := codec.ReadRequest() - if err == io.EOF { - codec.Close() - return - } else if err != nil { - glog.V(logger.Error).Infof("IPC recv err - %v\n", err) - codec.Close() - return - } - - var rpcResponse interface{} - res, err := api.Execute(req) - - rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) - err = codec.WriteResponse(rpcResponse) - if err != nil { - glog.V(logger.Error).Infof("IPC send err - %v\n", err) - codec.Close() - return - } - } - }(conn) - } - }() - - glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) - - return nil -} +// +build windows + +package comms + +import ( + "fmt" + "io" + "net" + "os" + "sync" + "syscall" + "time" + "unsafe" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rpc/api" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") + procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") + procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") + procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") +) + +func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func disconnectNamedPipe(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func waitNamedPipe(name *uint16, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if initialState { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +const ( + // openMode + pipe_access_duplex = 0x3 + pipe_access_inbound = 0x1 + pipe_access_outbound = 0x2 + + // openMode write flags + file_flag_first_pipe_instance = 0x00080000 + file_flag_write_through = 0x80000000 + file_flag_overlapped = 0x40000000 + + // openMode ACL flags + write_dac = 0x00040000 + write_owner = 0x00080000 + access_system_security = 0x01000000 + + // pipeMode + pipe_type_byte = 0x0 + pipe_type_message = 0x4 + + // pipeMode read mode flags + pipe_readmode_byte = 0x0 + pipe_readmode_message = 0x2 + + // pipeMode wait mode flags + pipe_wait = 0x0 + pipe_nowait = 0x1 + + // pipeMode remote-client mode flags + pipe_accept_remote_clients = 0x0 + pipe_reject_remote_clients = 0x8 + + pipe_unlimited_instances = 255 + + nmpwait_wait_forever = 0xFFFFFFFF + + // the two not-an-errors below occur if a client connects to the pipe between + // the server's CreateNamedPipe and ConnectNamedPipe calls. + error_no_data syscall.Errno = 0xE8 + error_pipe_connected syscall.Errno = 0x217 + error_pipe_busy syscall.Errno = 0xE7 + error_sem_timeout syscall.Errno = 0x79 + + error_bad_pathname syscall.Errno = 0xA1 + error_invalid_name syscall.Errno = 0x7B + + error_io_incomplete syscall.Errno = 0x3e4 +) + +var _ net.Conn = (*PipeConn)(nil) +var _ net.Listener = (*PipeListener)(nil) + +// ErrClosed is the error returned by PipeListener.Accept when Close is called +// on the PipeListener. +var ErrClosed = PipeError{"Pipe has been closed.", false} + +// PipeError is an error related to a call to a pipe +type PipeError struct { + msg string + timeout bool +} + +// Error implements the error interface +func (e PipeError) Error() string { + return e.msg +} + +// Timeout implements net.AddrError.Timeout() +func (e PipeError) Timeout() bool { + return e.timeout +} + +// Temporary implements net.AddrError.Temporary() +func (e PipeError) Temporary() bool { + return false +} + +// Dial connects to a named pipe with the given address. If the specified pipe is not available, +// it will wait indefinitely for the pipe to become available. +// +// The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ +// for remote pipes. +// +// Dial will return a PipeError if you pass in a badly formatted pipe name. +// +// Examples: +// // local pipe +// conn, err := Dial(`\\.\pipe\mypipename`) +// +// // remote pipe +// conn, err := Dial(`\\othercomp\pipe\mypipename`) +func Dial(address string) (*PipeConn, error) { + for { + conn, err := dial(address, nmpwait_wait_forever) + if err == nil { + return conn, nil + } + if isPipeNotReady(err) { + <-time.After(100 * time.Millisecond) + continue + } + return nil, err + } +} + +// DialTimeout acts like Dial, but will time out after the duration of timeout +func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) { + deadline := time.Now().Add(timeout) + + now := time.Now() + for now.Before(deadline) { + millis := uint32(deadline.Sub(now) / time.Millisecond) + conn, err := dial(address, millis) + if err == nil { + return conn, nil + } + if err == error_sem_timeout { + // This is WaitNamedPipe's timeout error, so we know we're done + return nil, PipeError{fmt.Sprintf( + "Timed out waiting for pipe '%s' to come available", address), true} + } + if isPipeNotReady(err) { + left := deadline.Sub(time.Now()) + retry := 100 * time.Millisecond + if left > retry { + <-time.After(retry) + } else { + <-time.After(left - time.Millisecond) + } + now = time.Now() + continue + } + return nil, err + } + return nil, PipeError{fmt.Sprintf( + "Timed out waiting for pipe '%s' to come available", address), true} +} + +// isPipeNotReady checks the error to see if it indicates the pipe is not ready +func isPipeNotReady(err error) bool { + // Pipe Busy means another client just grabbed the open pipe end, + // and the server hasn't made a new one yet. + // File Not Found means the server hasn't created the pipe yet. + // Neither is a fatal error. + + return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy +} + +// newOverlapped creates a structure used to track asynchronous +// I/O requests that have been issued. +func newOverlapped() (*syscall.Overlapped, error) { + event, err := createEvent(nil, true, true, nil) + if err != nil { + return nil, err + } + return &syscall.Overlapped{HEvent: event}, nil +} + +// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete. +// This function returns the number of bytes transferred by the operation and an error code if +// applicable (nil otherwise). +func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) { + _, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE) + if err != nil { + return 0, err + } + var transferred uint32 + err = getOverlappedResult(handle, overlapped, &transferred, true) + return transferred, err +} + +// dial is a helper to initiate a connection to a named pipe that has been started by a server. +// The timeout is only enforced if the pipe server has already created the pipe, otherwise +// this function will return immediately. +func dial(address string, timeout uint32) (*PipeConn, error) { + name, err := syscall.UTF16PtrFromString(string(address)) + if err != nil { + return nil, err + } + // If at least one instance of the pipe has been created, this function + // will wait timeout milliseconds for it to become available. + // It will return immediately regardless of timeout, if no instances + // of the named pipe have been created yet. + // If this returns with no error, there is a pipe available. + if err := waitNamedPipe(name, timeout); err != nil { + if err == error_bad_pathname { + // badly formatted pipe name + return nil, badAddr(address) + } + return nil, err + } + pathp, err := syscall.UTF16PtrFromString(address) + if err != nil { + return nil, err + } + handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, + uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, + syscall.FILE_FLAG_OVERLAPPED, 0) + if err != nil { + return nil, err + } + return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil +} + +// Listen returns a new PipeListener that will listen on a pipe with the given +// address. The address must be of the form \\.\pipe\ +// +// Listen will return a PipeError for an incorrectly formatted pipe name. +func Listen(address string) (*PipeListener, error) { + handle, err := createPipe(address, true) + if err == error_invalid_name { + return nil, badAddr(address) + } + if err != nil { + return nil, err + } + return &PipeListener{ + addr: PipeAddr(address), + handle: handle, + }, nil +} + +// PipeListener is a named pipe listener. Clients should typically +// use variables of type net.Listener instead of assuming named pipe. +type PipeListener struct { + addr PipeAddr + handle syscall.Handle + closed bool + + // acceptHandle contains the current handle waiting for + // an incoming connection or nil. + acceptHandle syscall.Handle + // acceptOverlapped is set before waiting on a connection. + // If not waiting, it is nil. + acceptOverlapped *syscall.Overlapped + // acceptMutex protects the handle and overlapped structure. + acceptMutex sync.Mutex +} + +// Accept implements the Accept method in the net.Listener interface; it +// waits for the next call and returns a generic net.Conn. +func (l *PipeListener) Accept() (net.Conn, error) { + c, err := l.AcceptPipe() + for err == error_no_data { + // Ignore clients that connect and immediately disconnect. + c, err = l.AcceptPipe() + } + if err != nil { + return nil, err + } + return c, nil +} + +// AcceptPipe accepts the next incoming call and returns the new connection. +// It might return an error if a client connected and immediately cancelled +// the connection. +func (l *PipeListener) AcceptPipe() (*PipeConn, error) { + if l == nil || l.addr == "" || l.closed { + return nil, syscall.EINVAL + } + + // the first time we call accept, the handle will have been created by the Listen + // call. This is to prevent race conditions where the client thinks the server + // isn't listening because it hasn't actually called create yet. After the first time, we'll + // have to create a new handle each time + handle := l.handle + if handle == 0 { + var err error + handle, err = createPipe(string(l.addr), false) + if err != nil { + return nil, err + } + } else { + l.handle = 0 + } + + overlapped, err := newOverlapped() + if err != nil { + return nil, err + } + defer syscall.CloseHandle(overlapped.HEvent) + if err := connectNamedPipe(handle, overlapped); err != nil && err != error_pipe_connected { + if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING { + l.acceptMutex.Lock() + l.acceptOverlapped = overlapped + l.acceptHandle = handle + l.acceptMutex.Unlock() + defer func() { + l.acceptMutex.Lock() + l.acceptOverlapped = nil + l.acceptHandle = 0 + l.acceptMutex.Unlock() + }() + + _, err = waitForCompletion(handle, overlapped) + } + if err == syscall.ERROR_OPERATION_ABORTED { + // Return error compatible to net.Listener.Accept() in case the + // listener was closed. + return nil, ErrClosed + } + if err != nil { + return nil, err + } + } + return &PipeConn{handle: handle, addr: l.addr}, nil +} + +// Close stops listening on the address. +// Already Accepted connections are not closed. +func (l *PipeListener) Close() error { + if l.closed { + return nil + } + l.closed = true + if l.handle != 0 { + err := disconnectNamedPipe(l.handle) + if err != nil { + return err + } + err = syscall.CloseHandle(l.handle) + if err != nil { + return err + } + l.handle = 0 + } + l.acceptMutex.Lock() + defer l.acceptMutex.Unlock() + if l.acceptOverlapped != nil && l.acceptHandle != 0 { + // Cancel the pending IO. This call does not block, so it is safe + // to hold onto the mutex above. + if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil { + return err + } + err := syscall.CloseHandle(l.acceptOverlapped.HEvent) + if err != nil { + return err + } + l.acceptOverlapped.HEvent = 0 + err = syscall.CloseHandle(l.acceptHandle) + if err != nil { + return err + } + l.acceptHandle = 0 + } + return nil +} + +// Addr returns the listener's network address, a PipeAddr. +func (l *PipeListener) Addr() net.Addr { return l.addr } + +// PipeConn is the implementation of the net.Conn interface for named pipe connections. +type PipeConn struct { + handle syscall.Handle + addr PipeAddr + + // these aren't actually used yet + readDeadline *time.Time + writeDeadline *time.Time +} + +type iodata struct { + n uint32 + err error +} + +// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to +// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending, +// the content of iodata is returned. +func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { + if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { + var timer <-chan time.Time + if deadline != nil { + if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { + timer = time.After(timeDiff) + } + } + done := make(chan iodata) + go func() { + n, err := waitForCompletion(c.handle, overlapped) + done <- iodata{n, err} + }() + select { + case data = <-done: + case <-timer: + syscall.CancelIoEx(c.handle, overlapped) + data = iodata{0, timeout(c.addr.String())} + } + } + // Windows will produce ERROR_BROKEN_PIPE upon closing + // a handle on the other end of a connection. Go RPC + // expects an io.EOF error in this case. + if data.err == syscall.ERROR_BROKEN_PIPE { + data.err = io.EOF + } + return int(data.n), data.err +} + +// Read implements the net.Conn Read method. +func (c *PipeConn) Read(b []byte) (int, error) { + // Use ReadFile() rather than Read() because the latter + // contains a workaround that eats ERROR_BROKEN_PIPE. + overlapped, err := newOverlapped() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(overlapped.HEvent) + var n uint32 + err = syscall.ReadFile(c.handle, b, &n, overlapped) + return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped) +} + +// Write implements the net.Conn Write method. +func (c *PipeConn) Write(b []byte) (int, error) { + overlapped, err := newOverlapped() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(overlapped.HEvent) + var n uint32 + err = syscall.WriteFile(c.handle, b, &n, overlapped) + return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped) +} + +// Close closes the connection. +func (c *PipeConn) Close() error { + return syscall.CloseHandle(c.handle) +} + +// LocalAddr returns the local network address. +func (c *PipeConn) LocalAddr() net.Addr { + return c.addr +} + +// RemoteAddr returns the remote network address. +func (c *PipeConn) RemoteAddr() net.Addr { + // not sure what to do here, we don't have remote addr.... + return c.addr +} + +// SetDeadline implements the net.Conn SetDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetDeadline(t time.Time) error { + c.SetReadDeadline(t) + c.SetWriteDeadline(t) + return nil +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetReadDeadline(t time.Time) error { + c.readDeadline = &t + return nil +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetWriteDeadline(t time.Time) error { + c.writeDeadline = &t + return nil +} + +// PipeAddr represents the address of a named pipe. +type PipeAddr string + +// Network returns the address's network name, "pipe". +func (a PipeAddr) Network() string { return "pipe" } + +// String returns the address of the pipe +func (a PipeAddr) String() string { + return string(a) +} + +// createPipe is a helper function to make sure we always create pipes +// with the same arguments, since subsequent calls to create pipe need +// to use the same arguments as the first one. If first is set, fail +// if the pipe already exists. +func createPipe(address string, first bool) (syscall.Handle, error) { + n, err := syscall.UTF16PtrFromString(address) + if err != nil { + return 0, err + } + mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED) + if first { + mode |= file_flag_first_pipe_instance + } + return createNamedPipe(n, + mode, + pipe_type_byte, + pipe_unlimited_instances, + 512, 512, 0, nil) +} + +func badAddr(addr string) PipeError { + return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false} +} +func timeout(addr string) PipeError { + return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} +} + +func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { + c, err := Dial(cfg.Endpoint) + if err != nil { + return nil, err + } + + return &ipcClient{codec.New(c)}, nil +} + +func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { + os.Remove(cfg.Endpoint) // in case it still exists from a previous run + + l, err := Listen(cfg.Endpoint) + if err != nil { + return err + } + os.Chmod(cfg.Endpoint, 0600) + + go func() { + for { + conn, err := l.Accept() + if err != nil { + glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err) + continue + } + + go func(conn net.Conn) { + codec := codec.New(conn) + + for { + req, err := codec.ReadRequest() + if err == io.EOF { + codec.Close() + return + } else if err != nil { + glog.V(logger.Error).Infof("IPC recv err - %v\n", err) + codec.Close() + return + } + + var rpcResponse interface{} + res, err := api.Execute(req) + + rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) + err = codec.WriteResponse(rpcResponse) + if err != nil { + glog.V(logger.Error).Infof("IPC send err - %v\n", err) + codec.Close() + return + } + } + }(conn) + } + }() + + glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) + + return nil +} diff --git a/rpc/jeth.go b/rpc/jeth.go index 0473adc4d..e578775bb 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -4,12 +4,13 @@ import ( "encoding/json" "fmt" + "reflect" + "github.com/ethereum/go-ethereum/jsre" - "github.com/robertkrimen/otto" - "github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/shared" - "reflect" + "github.com/robertkrimen/otto" ) type Jeth struct { diff --git a/rpc/shared/types.go b/rpc/shared/types.go index 600d39541..6a29fa88e 100644 --- a/rpc/shared/types.go +++ b/rpc/shared/types.go @@ -2,6 +2,7 @@ package shared import ( "encoding/json" + "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" ) @@ -45,15 +46,15 @@ func NewRpcResponse(id interface{}, jsonrpcver string, reply interface{}, err er var response interface{} switch err.(type) { - case nil: + case nil: response = &SuccessResponse{Jsonrpc: jsonrpcver, Id: id, Result: reply} - case *NotImplementedError: + case *NotImplementedError: jsonerr := &ErrorObject{-32601, err.Error()} response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} - case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: + case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: jsonerr := &ErrorObject{-32602, err.Error()} response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} - default: + default: jsonerr := &ErrorObject{-32603, err.Error()} response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} } -- cgit v1.2.3 From 08d72a9245ce6f1e11f84a6b59d66cb083bea9f9 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Tue, 9 Jun 2015 10:59:44 +0200 Subject: added personal API --- rpc/api/api.go | 31 +++++++++---- rpc/api/personal.go | 118 +++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/personal_args.go | 81 ++++++++++++++++++++++++++++++++ rpc/api/personal_js.go | 34 ++++++++++++++ rpc/api/utils.go | 4 ++ 5 files changed, 259 insertions(+), 9 deletions(-) create mode 100644 rpc/api/personal.go create mode 100644 rpc/api/personal_args.go create mode 100644 rpc/api/personal_js.go (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index 067a4d4e8..d2c548ed1 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -1,17 +1,30 @@ package api -import "github.com/ethereum/go-ethereum/rpc/shared" +import ( + "strings" + + "github.com/ethereum/go-ethereum/rpc/shared" +) const ( + EthApiName = "eth" + DebugApiName = "debug" + MergedApiName = "merged" + MinerApiName = "miner" + NetApiName = "net" + PersonalApiName = "personal" + Web3ApiName = "web3" +) + +var ( // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = "debug,eth,miner,net,web3" - - EthApiName = "eth" - DebugApiName = "debug" - MergedApiName = "merged" - MinerApiName = "miner" - NetApiName = "net" - Web3ApiName = "web3" + DefaultIpcApis = strings.Join([]string{ + EthApiName, + DebugApiName, + MinerApiName, + NetApiName, + PersonalApiName, + }, ",") ) // Ethereum RPC API interface diff --git a/rpc/api/personal.go b/rpc/api/personal.go new file mode 100644 index 000000000..d00363627 --- /dev/null +++ b/rpc/api/personal.go @@ -0,0 +1,118 @@ +package api + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +var ( + // mapping between methods and handlers + personalMapping = map[string]personalhandler{ + "personal_listAccounts": (*personal).ListAccounts, + "personal_newAccount": (*personal).NewAccount, + "personal_deleteAccount": (*personal).DeleteAccount, + "personal_unlockAccount": (*personal).UnlockAccount, + } +) + +// net callback handler +type personalhandler func(*personal, *shared.Request) (interface{}, error) + +// net api provider +type personal struct { + xeth *xeth.XEth + ethereum *eth.Ethereum + methods map[string]personalhandler + codec codec.ApiCoder +} + +// create a new net api instance +func NewPersonal(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *personal { + return &personal{ + xeth: xeth, + ethereum: eth, + methods: personalMapping, + codec: coder.New(nil), + } +} + +// collection with supported methods +func (self *personal) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *personal) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, shared.NewNotImplementedError(req.Method) +} + +func (self *personal) Name() string { + return PersonalApiName +} + +func (self *personal) ListAccounts(req *shared.Request) (interface{}, error) { + return self.xeth.Accounts(), nil +} + +func (self *personal) NewAccount(req *shared.Request) (interface{}, error) { + args := new(NewAccountArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + am := self.ethereum.AccountManager() + acc, err := am.NewAccount(args.Passphrase) + return acc.Address.Hex(), err +} + +func (self *personal) DeleteAccount(req *shared.Request) (interface{}, error) { + args := new(DeleteAccountArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + addr := common.HexToAddress(args.Address) + am := self.ethereum.AccountManager() + if err := am.DeleteAccount(addr, args.Passphrase); err == nil { + return true, nil + } else { + return false, err + } +} + +func (self *personal) UnlockAccount(req *shared.Request) (interface{}, error) { + args := new(UnlockAccountArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + var err error + am := self.ethereum.AccountManager() + addr := common.HexToAddress(args.Address) + + if args.Duration == -1 { + err = am.Unlock(addr, args.Passphrase) + } else { + err = am.TimedUnlock(addr, args.Passphrase, time.Duration(args.Duration)*time.Second) + } + + if err == nil { + return true, nil + } + return false, err +} diff --git a/rpc/api/personal_args.go b/rpc/api/personal_args.go new file mode 100644 index 000000000..b41fc06e7 --- /dev/null +++ b/rpc/api/personal_args.go @@ -0,0 +1,81 @@ +package api + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/rpc/shared" +) + +type NewAccountArgs struct { + Passphrase string +} + +func (args *NewAccountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + passhrase, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("passhrase", "not a string") + } + args.Passphrase = passhrase + + return nil +} + +type DeleteAccountArgs struct { + Address string + Passphrase string +} + +func (args *DeleteAccountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + addr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addr + + passhrase, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("passhrase", "not a string") + } + args.Passphrase = passhrase + + return nil +} + +type UnlockAccountArgs struct { + Address string + Passphrase string + Duration int +} + +func (args *UnlockAccountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + args.Duration = -1 + + addrstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("address", "not a string") + } + args.Address = addrstr + + passphrasestr, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("passphrase", "not a string") + } + args.Passphrase = passphrasestr + + return nil +} diff --git a/rpc/api/personal_js.go b/rpc/api/personal_js.go new file mode 100644 index 000000000..7fd9a2dea --- /dev/null +++ b/rpc/api/personal_js.go @@ -0,0 +1,34 @@ +package api + +const Personal_JS = ` +web3.extend({ + property: 'personal', + methods: + [ + new web3.extend.Method({ + name: 'listAccounts', + call: 'personal_listAccounts', + params: 0, + inputFormatter: [], + outputFormatter: function(obj) { return obj; } + }), + new web3.extend.Method({ + name: 'newAccount', + call: 'personal_newAccount', + params: 1, + inputFormatter: [web3.extend.formatters.formatInputString], + outputFormatter: web3.extend.formatters.formatOutputString + }), + new web3.extend.Method({ + name: 'unlockAccount', + call: 'personal_unlockAccount', + params: 3, + inputFormatter: [web3.extend.formatters.formatInputString,web3.extend.formatters.formatInputString,web3.extend.formatters.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputBool + }) + ], + properties: + [ + ] +}); +` diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 6e6d5c7b0..eae23d351 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -29,6 +29,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. apis[i] = NewMinerApi(eth, codec) case NetApiName: apis[i] = NewNetApi(xeth, eth, codec) + case PersonalApiName: + apis[i] = NewPersonal(xeth, eth, codec) case Web3ApiName: apis[i] = NewWeb3(xeth, codec) default: @@ -47,6 +49,8 @@ func Javascript(name string) string { return Miner_JS case NetApiName: return Net_JS + case PersonalApiName: + return Personal_JS } return "" -- cgit v1.2.3 From cc9ae399338557b6671e8fc83bb696c5ddb068fe Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Tue, 9 Jun 2015 16:06:51 +0200 Subject: added admin API --- rpc/api/admin.go | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/admin_args.go | 107 +++++++++++++++++++++++ rpc/api/admin_js.go | 67 +++++++++++++++ rpc/api/api.go | 7 +- rpc/api/debug.go | 38 ++++----- rpc/api/eth.go | 180 +++++++++++++++++++-------------------- rpc/api/eth_args.go | 30 +++---- rpc/api/mergedapi.go | 14 ++-- rpc/api/miner.go | 46 +++++----- rpc/api/net.go | 30 +++---- rpc/api/net_js.go | 7 ++ rpc/api/personal.go | 30 +++---- rpc/api/utils.go | 8 +- rpc/api/web3.go | 24 +++--- 14 files changed, 613 insertions(+), 203 deletions(-) create mode 100644 rpc/api/admin.go create mode 100644 rpc/api/admin_args.go create mode 100644 rpc/api/admin_js.go (limited to 'rpc') diff --git a/rpc/api/admin.go b/rpc/api/admin.go new file mode 100644 index 000000000..c37463604 --- /dev/null +++ b/rpc/api/admin.go @@ -0,0 +1,228 @@ +package api + +import ( + "fmt" + "io" + "os" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +const ( + AdminVersion = "1.0.0" + importBatchSize = 2500 +) + +var ( + // mapping between methods and handlers + AdminMapping = map[string]adminhandler{ + // "admin_startRPC": (*adminApi).StartRPC, + // "admin_stopRPC": (*adminApi).StopRPC, + "admin_addPeer": (*adminApi).AddPeer, + "admin_peers": (*adminApi).Peers, + "admin_nodeInfo": (*adminApi).NodeInfo, + "admin_exportChain": (*adminApi).ExportChain, + "admin_importChain": (*adminApi).ImportChain, + "admin_verbosity": (*adminApi).Verbosity, + "admin_syncStatus": (*adminApi).SyncStatus, + "admin_setSolc": (*adminApi).SetSolc, + } +) + +// admin callback handler +type adminhandler func(*adminApi, *shared.Request) (interface{}, error) + +// admin api provider +type adminApi struct { + xeth *xeth.XEth + ethereum *eth.Ethereum + methods map[string]adminhandler + codec codec.ApiCoder +} + +// create a new admin api instance +func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *adminApi { + return &adminApi{ + xeth: xeth, + ethereum: ethereum, + methods: AdminMapping, + codec: coder.New(nil), + } +} + +// collection with supported methods +func (self *adminApi) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *adminApi) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, &shared.NotImplementedError{req.Method} +} + +func (self *adminApi) Name() string { + return AdminApiName +} + +func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) { + args := new(AddPeerArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + err := self.ethereum.AddPeer(args.Url) + if err == nil { + return true, nil + } + return false, err +} + +func (self *adminApi) Peers(req *shared.Request) (interface{}, error) { + return self.ethereum.PeersInfo(), nil +} + +func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) { + return false, nil + // Enable when http rpc interface is refactored to prevent import cycles + // args := new(StartRpcArgs) + // if err := self.codec.Decode(req.Params, &args); err != nil { + // return nil, shared.NewDecodeParamError(err.Error()) + // } + // + // cfg := rpc.RpcConfig{ + // ListenAddress: args.Address, + // ListenPort: args.Port, + // } + // + // err := rpc.Start(self.xeth, cfg) + // if err == nil { + // return true, nil + // } + // return false, err +} + +func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) { + return false, nil + // Enable when http rpc interface is refactored to prevent import cycles + // rpc.Stop() + // return true, nil +} + +func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) { + return self.ethereum.NodeInfo(), nil +} + +func hasAllBlocks(chain *core.ChainManager, bs []*types.Block) bool { + for _, b := range bs { + if !chain.HasBlock(b.Hash()) { + return false + } + } + return true +} + +func (self *adminApi) ImportChain(req *shared.Request) (interface{}, error) { + args := new(ImportExportChainArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + fh, err := os.Open(args.Filename) + if err != nil { + return false, err + } + defer fh.Close() + stream := rlp.NewStream(fh, 0) + + // Run actual the import. + blocks := make(types.Blocks, importBatchSize) + n := 0 + for batch := 0; ; batch++ { + + i := 0 + for ; i < importBatchSize; i++ { + var b types.Block + if err := stream.Decode(&b); err == io.EOF { + break + } else if err != nil { + return false, fmt.Errorf("at block %d: %v", n, err) + } + blocks[i] = &b + n++ + } + if i == 0 { + break + } + // Import the batch. + if hasAllBlocks(self.ethereum.ChainManager(), blocks[:i]) { + continue + } + if _, err := self.ethereum.ChainManager().InsertChain(blocks[:i]); err != nil { + return false, fmt.Errorf("invalid block %d: %v", n, err) + } + } + return true, nil +} + +func (self *adminApi) ExportChain(req *shared.Request) (interface{}, error) { + args := new(ImportExportChainArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + fh, err := os.OpenFile(args.Filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) + if err != nil { + return false, err + } + defer fh.Close() + if err := self.ethereum.ChainManager().Export(fh); err != nil { + return false, err + } + + return true, nil +} + +func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) { + args := new(VerbosityArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + glog.SetV(args.Level) + return true, nil +} + +func (self *adminApi) SyncStatus(req *shared.Request) (interface{}, error) { + pending, cached := self.ethereum.Downloader().Stats() + return map[string]interface{}{"available": pending, "waitingForImport": cached}, nil +} + +func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) { + args := new(SetSolcArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + solc, err := self.xeth.SetSolc(args.Path) + if err != nil { + return nil, err + } + return solc.Info(), nil +} diff --git a/rpc/api/admin_args.go b/rpc/api/admin_args.go new file mode 100644 index 000000000..9c0cbdcb6 --- /dev/null +++ b/rpc/api/admin_args.go @@ -0,0 +1,107 @@ +package api + +import ( + "encoding/json" + + "math/big" + + "github.com/ethereum/go-ethereum/rpc/shared" +) + +type AddPeerArgs struct { + Url string +} + +func (args *AddPeerArgs) 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.NewDecodeParamError("Expected enode as argument") + } + + urlstr, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("url", "not a string") + } + args.Url = urlstr + + return nil +} + +type ImportExportChainArgs struct { + Filename string +} + +func (args *ImportExportChainArgs) 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.NewDecodeParamError("Expected filename as argument") + } + + filename, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("filename", "not a string") + } + args.Filename = filename + + return nil +} + +type VerbosityArgs struct { + Level int +} + +func (args *VerbosityArgs) 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.NewDecodeParamError("Expected enode as argument") + } + + if levelint, ok := obj[0].(int); ok { + args.Level = levelint + } else if levelstr, ok := obj[0].(string); ok { + if !ok { + return shared.NewInvalidTypeError("level", "not a string") + } + level, success := new(big.Int).SetString(levelstr, 0) + if !success { + return shared.NewDecodeParamError("Unable to parse verbosity level") + } + args.Level = int(level.Int64()) + } + + return nil +} + +type SetSolcArgs struct { + Path string +} + +func (args *SetSolcArgs) 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.NewDecodeParamError("Expected path as argument") + } + + if pathstr, ok := obj[0].(string); ok { + args.Path = pathstr + return nil + } + + return shared.NewInvalidTypeError("path", "not a string") +} diff --git a/rpc/api/admin_js.go b/rpc/api/admin_js.go new file mode 100644 index 000000000..02a0e93e1 --- /dev/null +++ b/rpc/api/admin_js.go @@ -0,0 +1,67 @@ +package api + +const Admin_JS = ` +web3.extend({ + property: 'admin', + methods: + [ + new web3.extend.Method({ + name: 'addPeer', + call: 'admin_addPeer', + params: 1, + inputFormatter: [web3.extend.utils.formatInputString], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'peers', + call: 'admin_peers', + params: 0, + inputFormatter: [], + outputFormatter: function(obj) { return obj; } + }), + new web3.extend.Method({ + name: 'exportChain', + call: 'admin_exportChain', + params: 1, + inputFormatter: [web3.extend.utils.formatInputString], + outputFormatter: function(obj) { return obj; } + }), + new web3.extend.Method({ + name: 'importChain', + call: 'admin_importChain', + params: 1, + inputFormatter: [web3.extend.utils.formatInputString], + outputFormatter: function(obj) { return obj; } + }), + new web3.extend.Method({ + name: 'verbosity', + call: 'admin_verbosity', + params: 1, + inputFormatter: [web3.extend.utils.formatInputInt], + outputFormatter: web3.extend.formatters.formatOutputBool + }), + new web3.extend.Method({ + name: 'syncStatus', + call: 'admin_syncStatus', + params: 1, + inputFormatter: [web3.extend.utils.formatInputInt], + outputFormatter: function(obj) { return obj; } + }), + new web3.extend.Method({ + name: 'setSolc', + call: 'admin_setSolc', + params: 1, + inputFormatter: [web3.extend.utils.formatInputString], + outputFormatter: web3.extend.formatters.formatOutputString + }) + ], + properties: + [ + new web3.extend.Property({ + name: 'nodeInfo', + getter: 'admin_nodeInfo', + outputFormatter: web3.extend.formatters.formatOutputString + }) + ] +}); +` diff --git a/rpc/api/api.go b/rpc/api/api.go index d2c548ed1..28b824658 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -7,6 +7,7 @@ import ( ) const ( + AdminApiName = "admin" EthApiName = "eth" DebugApiName = "debug" MergedApiName = "merged" @@ -19,11 +20,7 @@ const ( var ( // List with all API's which are offered over the IPC interface by default DefaultIpcApis = strings.Join([]string{ - EthApiName, - DebugApiName, - MinerApiName, - NetApiName, - PersonalApiName, + AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, PersonalApiName, Web3ApiName, }, ",") ) diff --git a/rpc/api/debug.go b/rpc/api/debug.go index 26f43fe74..2930ad870 100644 --- a/rpc/api/debug.go +++ b/rpc/api/debug.go @@ -20,20 +20,20 @@ const ( var ( // mapping between methods and handlers DebugMapping = map[string]debughandler{ - "debug_dumpBlock": (*DebugApi).DumpBlock, - "debug_getBlockRlp": (*DebugApi).GetBlockRlp, - "debug_printBlock": (*DebugApi).PrintBlock, - "debug_processBlock": (*DebugApi).ProcessBlock, - "debug_seedHash": (*DebugApi).SeedHash, - "debug_setHead": (*DebugApi).SetHead, + "debug_dumpBlock": (*debugApi).DumpBlock, + "debug_getBlockRlp": (*debugApi).GetBlockRlp, + "debug_printBlock": (*debugApi).PrintBlock, + "debug_processBlock": (*debugApi).ProcessBlock, + "debug_seedHash": (*debugApi).SeedHash, + "debug_setHead": (*debugApi).SetHead, } ) // debug callback handler -type debughandler func(*DebugApi, *shared.Request) (interface{}, error) +type debughandler func(*debugApi, *shared.Request) (interface{}, error) // admin api provider -type DebugApi struct { +type debugApi struct { xeth *xeth.XEth ethereum *eth.Ethereum methods map[string]debughandler @@ -41,8 +41,8 @@ type DebugApi struct { } // create a new debug api instance -func NewDebugApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *DebugApi { - return &DebugApi{ +func NewDebugApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *debugApi { + return &debugApi{ xeth: xeth, ethereum: ethereum, methods: DebugMapping, @@ -51,7 +51,7 @@ func NewDebugApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *De } // collection with supported methods -func (self *DebugApi) Methods() []string { +func (self *debugApi) Methods() []string { methods := make([]string, len(self.methods)) i := 0 for k := range self.methods { @@ -62,7 +62,7 @@ func (self *DebugApi) Methods() []string { } // Execute given request -func (self *DebugApi) Execute(req *shared.Request) (interface{}, error) { +func (self *debugApi) Execute(req *shared.Request) (interface{}, error) { if callback, ok := self.methods[req.Method]; ok { return callback(self, req) } @@ -70,11 +70,11 @@ func (self *DebugApi) Execute(req *shared.Request) (interface{}, error) { return nil, &shared.NotImplementedError{req.Method} } -func (self *DebugApi) Name() string { +func (self *debugApi) Name() string { return DebugApiName } -func (self *DebugApi) PrintBlock(req *shared.Request) (interface{}, error) { +func (self *debugApi) PrintBlock(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -84,7 +84,7 @@ func (self *DebugApi) PrintBlock(req *shared.Request) (interface{}, error) { return fmt.Sprintf("%s", block), nil } -func (self *DebugApi) DumpBlock(req *shared.Request) (interface{}, error) { +func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -103,7 +103,7 @@ func (self *DebugApi) DumpBlock(req *shared.Request) (interface{}, error) { return stateDb.Dump(), nil } -func (self *DebugApi) GetBlockRlp(req *shared.Request) (interface{}, error) { +func (self *debugApi) GetBlockRlp(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -117,7 +117,7 @@ func (self *DebugApi) GetBlockRlp(req *shared.Request) (interface{}, error) { return fmt.Sprintf("%x", encoded), err } -func (self *DebugApi) SetHead(req *shared.Request) (interface{}, error) { +func (self *debugApi) SetHead(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -133,7 +133,7 @@ func (self *DebugApi) SetHead(req *shared.Request) (interface{}, error) { return nil, nil } -func (self *DebugApi) ProcessBlock(req *shared.Request) (interface{}, error) { +func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -155,7 +155,7 @@ func (self *DebugApi) ProcessBlock(req *shared.Request) (interface{}, error) { return false, err } -func (self *DebugApi) SeedHash(req *shared.Request) (interface{}, error) { +func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) diff --git a/rpc/api/eth.go b/rpc/api/eth.go index 0a8cecdbc..f27f17f39 100644 --- a/rpc/api/eth.go +++ b/rpc/api/eth.go @@ -13,68 +13,68 @@ import ( // eth api provider // See https://github.com/ethereum/wiki/wiki/JSON-RPC -type EthApi struct { +type ethApi struct { xeth *xeth.XEth methods map[string]ethhandler codec codec.ApiCoder } // eth callback handler -type ethhandler func(*EthApi, *shared.Request) (interface{}, error) +type ethhandler func(*ethApi, *shared.Request) (interface{}, error) var ( ethMapping = map[string]ethhandler{ - "eth_accounts": (*EthApi).Accounts, - "eth_blockNumber": (*EthApi).BlockNumber, - "eth_getBalance": (*EthApi).GetBalance, - "eth_protocolVersion": (*EthApi).ProtocolVersion, - "eth_coinbase": (*EthApi).Coinbase, - "eth_mining": (*EthApi).IsMining, - "eth_gasPrice": (*EthApi).GasPrice, - "eth_getStorage": (*EthApi).GetStorage, - "eth_storageAt": (*EthApi).GetStorage, - "eth_getStorageAt": (*EthApi).GetStorageAt, - "eth_getTransactionCount": (*EthApi).GetTransactionCount, - "eth_getBlockTransactionCountByHash": (*EthApi).GetBlockTransactionCountByHash, - "eth_getBlockTransactionCountByNumber": (*EthApi).GetBlockTransactionCountByNumber, - "eth_getUncleCountByBlockHash": (*EthApi).GetUncleCountByBlockHash, - "eth_getUncleCountByBlockNumber": (*EthApi).GetUncleCountByBlockNumber, - "eth_getData": (*EthApi).GetData, - "eth_getCode": (*EthApi).GetData, - "eth_sign": (*EthApi).Sign, - "eth_sendTransaction": (*EthApi).SendTransaction, - "eth_transact": (*EthApi).SendTransaction, - "eth_estimateGas": (*EthApi).EstimateGas, - "eth_call": (*EthApi).Call, - "eth_flush": (*EthApi).Flush, - "eth_getBlockByHash": (*EthApi).GetBlockByHash, - "eth_getBlockByNumber": (*EthApi).GetBlockByNumber, - "eth_getTransactionByHash": (*EthApi).GetTransactionByHash, - "eth_getTransactionByBlockHashAndIndex": (*EthApi).GetTransactionByBlockHashAndIndex, - "eth_getUncleByBlockHashAndIndex": (*EthApi).GetUncleByBlockHashAndIndex, - "eth_getUncleByBlockNumberAndIndex": (*EthApi).GetUncleByBlockNumberAndIndex, - "eth_getCompilers": (*EthApi).GetCompilers, - "eth_compileSolidity": (*EthApi).CompileSolidity, - "eth_newFilter": (*EthApi).NewFilter, - "eth_newBlockFilter": (*EthApi).NewBlockFilter, - "eth_newPendingTransactionFilter": (*EthApi).NewPendingTransactionFilter, - "eth_uninstallFilter": (*EthApi).UninstallFilter, - "eth_getFilterChanges": (*EthApi).GetFilterChanges, - "eth_getFilterLogs": (*EthApi).GetFilterLogs, - "eth_getLogs": (*EthApi).GetLogs, - "eth_hashrate": (*EthApi).Hashrate, - "eth_getWork": (*EthApi).GetWork, - "eth_submitWork": (*EthApi).SubmitWork, + "eth_accounts": (*ethApi).Accounts, + "eth_blockNumber": (*ethApi).BlockNumber, + "eth_getBalance": (*ethApi).GetBalance, + "eth_protocolVersion": (*ethApi).ProtocolVersion, + "eth_coinbase": (*ethApi).Coinbase, + "eth_mining": (*ethApi).IsMining, + "eth_gasPrice": (*ethApi).GasPrice, + "eth_getStorage": (*ethApi).GetStorage, + "eth_storageAt": (*ethApi).GetStorage, + "eth_getStorageAt": (*ethApi).GetStorageAt, + "eth_getTransactionCount": (*ethApi).GetTransactionCount, + "eth_getBlockTransactionCountByHash": (*ethApi).GetBlockTransactionCountByHash, + "eth_getBlockTransactionCountByNumber": (*ethApi).GetBlockTransactionCountByNumber, + "eth_getUncleCountByBlockHash": (*ethApi).GetUncleCountByBlockHash, + "eth_getUncleCountByBlockNumber": (*ethApi).GetUncleCountByBlockNumber, + "eth_getData": (*ethApi).GetData, + "eth_getCode": (*ethApi).GetData, + "eth_sign": (*ethApi).Sign, + "eth_sendTransaction": (*ethApi).SendTransaction, + "eth_transact": (*ethApi).SendTransaction, + "eth_estimateGas": (*ethApi).EstimateGas, + "eth_call": (*ethApi).Call, + "eth_flush": (*ethApi).Flush, + "eth_getBlockByHash": (*ethApi).GetBlockByHash, + "eth_getBlockByNumber": (*ethApi).GetBlockByNumber, + "eth_getTransactionByHash": (*ethApi).GetTransactionByHash, + "eth_getTransactionByBlockHashAndIndex": (*ethApi).GetTransactionByBlockHashAndIndex, + "eth_getUncleByBlockHashAndIndex": (*ethApi).GetUncleByBlockHashAndIndex, + "eth_getUncleByBlockNumberAndIndex": (*ethApi).GetUncleByBlockNumberAndIndex, + "eth_getCompilers": (*ethApi).GetCompilers, + "eth_compileSolidity": (*ethApi).CompileSolidity, + "eth_newFilter": (*ethApi).NewFilter, + "eth_newBlockFilter": (*ethApi).NewBlockFilter, + "eth_newPendingTransactionFilter": (*ethApi).NewPendingTransactionFilter, + "eth_uninstallFilter": (*ethApi).UninstallFilter, + "eth_getFilterChanges": (*ethApi).GetFilterChanges, + "eth_getFilterLogs": (*ethApi).GetFilterLogs, + "eth_getLogs": (*ethApi).GetLogs, + "eth_hashrate": (*ethApi).Hashrate, + "eth_getWork": (*ethApi).GetWork, + "eth_submitWork": (*ethApi).SubmitWork, } ) -// create new EthApi instance -func NewEthApi(xeth *xeth.XEth, codec codec.Codec) *EthApi { - return &EthApi{xeth, ethMapping, codec.New(nil)} +// create new ethApi instance +func NewEthApi(xeth *xeth.XEth, codec codec.Codec) *ethApi { + return ðApi{xeth, ethMapping, codec.New(nil)} } // collection with supported methods -func (self *EthApi) Methods() []string { +func (self *ethApi) Methods() []string { methods := make([]string, len(self.methods)) i := 0 for k := range self.methods { @@ -85,7 +85,7 @@ func (self *EthApi) Methods() []string { } // Execute given request -func (self *EthApi) Execute(req *shared.Request) (interface{}, error) { +func (self *ethApi) Execute(req *shared.Request) (interface{}, error) { if callback, ok := self.methods[req.Method]; ok { return callback(self, req) } @@ -93,23 +93,23 @@ func (self *EthApi) Execute(req *shared.Request) (interface{}, error) { return nil, shared.NewNotImplementedError(req.Method) } -func (self *EthApi) Name() string { +func (self *ethApi) Name() string { return EthApiName } -func (self *EthApi) Accounts(req *shared.Request) (interface{}, error) { +func (self *ethApi) Accounts(req *shared.Request) (interface{}, error) { return self.xeth.Accounts(), nil } -func (self *EthApi) Hashrate(req *shared.Request) (interface{}, error) { +func (self *ethApi) Hashrate(req *shared.Request) (interface{}, error) { return newHexNum(self.xeth.HashRate()), nil } -func (self *EthApi) BlockNumber(req *shared.Request) (interface{}, error) { +func (self *ethApi) BlockNumber(req *shared.Request) (interface{}, error) { return self.xeth.CurrentBlock().Number(), nil } -func (self *EthApi) GetBalance(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetBalance(req *shared.Request) (interface{}, error) { args := new(GetBalanceArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -118,23 +118,23 @@ func (self *EthApi) GetBalance(req *shared.Request) (interface{}, error) { return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil } -func (self *EthApi) ProtocolVersion(req *shared.Request) (interface{}, error) { +func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) { return self.xeth.EthVersion(), nil } -func (self *EthApi) Coinbase(req *shared.Request) (interface{}, error) { +func (self *ethApi) Coinbase(req *shared.Request) (interface{}, error) { return newHexData(self.xeth.Coinbase()), nil } -func (self *EthApi) IsMining(req *shared.Request) (interface{}, error) { +func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) { return self.xeth.IsMining(), nil } -func (self *EthApi) GasPrice(req *shared.Request) (interface{}, error) { +func (self *ethApi) GasPrice(req *shared.Request) (interface{}, error) { return newHexNum(xeth.DefaultGasPrice().Bytes()), nil } -func (self *EthApi) GetStorage(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetStorage(req *shared.Request) (interface{}, error) { args := new(GetStorageArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -143,7 +143,7 @@ func (self *EthApi) GetStorage(req *shared.Request) (interface{}, error) { return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil } -func (self *EthApi) GetStorageAt(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) { args := new(GetStorageAtArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -152,7 +152,7 @@ func (self *EthApi) GetStorageAt(req *shared.Request) (interface{}, error) { return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil } -func (self *EthApi) GetTransactionCount(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) { args := new(GetTxCountArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -162,7 +162,7 @@ func (self *EthApi) GetTransactionCount(req *shared.Request) (interface{}, error return newHexNum(big.NewInt(int64(count)).Bytes()), nil } -func (self *EthApi) GetBlockTransactionCountByHash(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetBlockTransactionCountByHash(req *shared.Request) (interface{}, error) { args := new(HashArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -176,7 +176,7 @@ func (self *EthApi) GetBlockTransactionCountByHash(req *shared.Request) (interfa } } -func (self *EthApi) GetBlockTransactionCountByNumber(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetBlockTransactionCountByNumber(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -190,7 +190,7 @@ func (self *EthApi) GetBlockTransactionCountByNumber(req *shared.Request) (inter } } -func (self *EthApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, error) { args := new(HashArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -204,7 +204,7 @@ func (self *EthApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, return newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes()), nil } -func (self *EthApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -218,7 +218,7 @@ func (self *EthApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{} return newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes()), nil } -func (self *EthApi) GetData(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetData(req *shared.Request) (interface{}, error) { args := new(GetDataArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -227,8 +227,8 @@ func (self *EthApi) GetData(req *shared.Request) (interface{}, error) { return newHexData(v), nil } -func (self *EthApi) Sign(req *shared.Request) (interface{}, error) { - args := new(NewSignArgs) +func (self *ethApi) Sign(req *shared.Request) (interface{}, error) { + args := new(NewSigArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) } @@ -239,7 +239,7 @@ func (self *EthApi) Sign(req *shared.Request) (interface{}, error) { return v, nil } -func (self *EthApi) SendTransaction(req *shared.Request) (interface{}, error) { +func (self *ethApi) SendTransaction(req *shared.Request) (interface{}, error) { args := new(NewTxArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -258,7 +258,7 @@ func (self *EthApi) SendTransaction(req *shared.Request) (interface{}, error) { return v, nil } -func (self *EthApi) EstimateGas(req *shared.Request) (interface{}, error) { +func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) { _, gas, err := self.doCall(req.Params) if err != nil { return nil, err @@ -272,7 +272,7 @@ func (self *EthApi) EstimateGas(req *shared.Request) (interface{}, error) { } } -func (self *EthApi) Call(req *shared.Request) (interface{}, error) { +func (self *ethApi) Call(req *shared.Request) (interface{}, error) { v, _, err := self.doCall(req.Params) if err != nil { return nil, err @@ -286,11 +286,11 @@ func (self *EthApi) Call(req *shared.Request) (interface{}, error) { } } -func (self *EthApi) Flush(req *shared.Request) (interface{}, error) { +func (self *ethApi) Flush(req *shared.Request) (interface{}, error) { return nil, shared.NewNotImplementedError(req.Method) } -func (self *EthApi) doCall(params json.RawMessage) (string, string, error) { +func (self *ethApi) doCall(params json.RawMessage) (string, string, error) { args := new(CallArgs) if err := self.codec.Decode(params, &args); err != nil { return "", "", err @@ -299,7 +299,7 @@ func (self *EthApi) doCall(params json.RawMessage) (string, string, error) { return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) } -func (self *EthApi) GetBlockByHash(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) { args := new(GetBlockByHashArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -309,7 +309,7 @@ func (self *EthApi) GetBlockByHash(req *shared.Request) (interface{}, error) { return NewBlockRes(block, args.IncludeTxs), nil } -func (self *EthApi) GetBlockByNumber(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) { args := new(GetBlockByNumberArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -328,7 +328,7 @@ func (self *EthApi) GetBlockByNumber(req *shared.Request) (interface{}, error) { return br, nil } -func (self *EthApi) GetTransactionByHash(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetTransactionByHash(req *shared.Request) (interface{}, error) { args := new(HashArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -348,7 +348,7 @@ func (self *EthApi) GetTransactionByHash(req *shared.Request) (interface{}, erro return nil, nil } -func (self *EthApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (interface{}, error) { args := new(HashIndexArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -367,7 +367,7 @@ func (self *EthApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (inte } } -func (self *EthApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { args := new(BlockNumIndexArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -386,7 +386,7 @@ func (self *EthApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (in return v.Transactions[args.Index], nil } -func (self *EthApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{}, error) { args := new(HashIndexArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -405,7 +405,7 @@ func (self *EthApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{ return br.Uncles[args.Index], nil } -func (self *EthApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { args := new(BlockNumIndexArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -425,7 +425,7 @@ func (self *EthApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interfac } } -func (self *EthApi) GetCompilers(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetCompilers(req *shared.Request) (interface{}, error) { var lang string if solc, _ := self.xeth.Solc(); solc != nil { lang = "Solidity" @@ -434,7 +434,7 @@ func (self *EthApi) GetCompilers(req *shared.Request) (interface{}, error) { return c, nil } -func (self *EthApi) CompileSolidity(req *shared.Request) (interface{}, error) { +func (self *ethApi) CompileSolidity(req *shared.Request) (interface{}, error) { solc, _ := self.xeth.Solc() if solc == nil { return nil, shared.NewNotAvailableError(req.Method, "solc (solidity compiler) not found") @@ -452,7 +452,7 @@ func (self *EthApi) CompileSolidity(req *shared.Request) (interface{}, error) { return contracts, nil } -func (self *EthApi) NewFilter(req *shared.Request) (interface{}, error) { +func (self *ethApi) NewFilter(req *shared.Request) (interface{}, error) { args := new(BlockFilterArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -462,15 +462,15 @@ func (self *EthApi) NewFilter(req *shared.Request) (interface{}, error) { return newHexNum(big.NewInt(int64(id)).Bytes()), nil } -func (self *EthApi) NewBlockFilter(req *shared.Request) (interface{}, error) { +func (self *ethApi) NewBlockFilter(req *shared.Request) (interface{}, error) { return newHexNum(self.xeth.NewBlockFilter()), nil } -func (self *EthApi) NewPendingTransactionFilter(req *shared.Request) (interface{}, error) { +func (self *ethApi) NewPendingTransactionFilter(req *shared.Request) (interface{}, error) { return newHexNum(self.xeth.NewTransactionFilter()), nil } -func (self *EthApi) UninstallFilter(req *shared.Request) (interface{}, error) { +func (self *ethApi) UninstallFilter(req *shared.Request) (interface{}, error) { args := new(FilterIdArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -478,7 +478,7 @@ func (self *EthApi) UninstallFilter(req *shared.Request) (interface{}, error) { return self.xeth.UninstallFilter(args.Id), nil } -func (self *EthApi) GetFilterChanges(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetFilterChanges(req *shared.Request) (interface{}, error) { args := new(FilterIdArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -496,7 +496,7 @@ func (self *EthApi) GetFilterChanges(req *shared.Request) (interface{}, error) { } } -func (self *EthApi) GetFilterLogs(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetFilterLogs(req *shared.Request) (interface{}, error) { args := new(FilterIdArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -505,7 +505,7 @@ func (self *EthApi) GetFilterLogs(req *shared.Request) (interface{}, error) { return NewLogsRes(self.xeth.Logs(args.Id)), nil } -func (self *EthApi) GetLogs(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetLogs(req *shared.Request) (interface{}, error) { args := new(BlockFilterArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -513,12 +513,12 @@ func (self *EthApi) GetLogs(req *shared.Request) (interface{}, error) { return NewLogsRes(self.xeth.AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)), nil } -func (self *EthApi) GetWork(req *shared.Request) (interface{}, error) { +func (self *ethApi) GetWork(req *shared.Request) (interface{}, error) { self.xeth.SetMining(true, 0) return self.xeth.RemoteMining().GetWork(), nil } -func (self *EthApi) SubmitWork(req *shared.Request) (interface{}, error) { +func (self *ethApi) SubmitWork(req *shared.Request) (interface{}, error) { args := new(SubmitWorkArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) diff --git a/rpc/api/eth_args.go b/rpc/api/eth_args.go index 1ef6f9efb..ad9a35fa2 100644 --- a/rpc/api/eth_args.go +++ b/rpc/api/eth_args.go @@ -226,19 +226,14 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { return nil } -type NewSignArgs struct { +type NewSigArgs struct { From string Data string } -func (args *NewSignArgs) UnmarshalJSON(b []byte) (err error) { - var obj []json.RawMessage - var ext struct { - From string - Data string - } +func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} - // Decode byte slice to array of RawMessages if err := json.Unmarshal(b, &obj); err != nil { return shared.NewDecodeParamError(err.Error()) } @@ -248,21 +243,26 @@ func (args *NewSignArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewInsufficientParamsError(len(obj), 1) } - // Decode 0th RawMessage to temporary struct - if err := json.Unmarshal(obj[0], &ext); err != nil { - return shared.NewDecodeParamError(err.Error()) + from, ok := obj[0].(string) + if !ok { + return shared.NewInvalidTypeError("from", "not a string") } + args.From = from - if len(ext.From) == 0 { + if len(args.From) == 0 { return shared.NewValidationError("from", "is required") } - if len(ext.Data) == 0 { + data, ok := obj[1].(string) + if !ok { + return shared.NewInvalidTypeError("data", "not a string") + } + args.Data = data + + if len(args.Data) == 0 { return shared.NewValidationError("data", "is required") } - args.From = ext.From - args.Data = ext.Data return nil } diff --git a/rpc/api/mergedapi.go b/rpc/api/mergedapi.go index 88c301aae..dea8d1289 100644 --- a/rpc/api/mergedapi.go +++ b/rpc/api/mergedapi.go @@ -3,14 +3,14 @@ package api import "github.com/ethereum/go-ethereum/rpc/shared" // combines multiple API's -type mergedApi struct { +type MergedApi struct { apis []string methods map[string]EthereumApi } // create new merged api instance -func newMergedApi(apis ...EthereumApi) *mergedApi { - mergedApi := new(mergedApi) +func newMergedApi(apis ...EthereumApi) *MergedApi { + mergedApi := new(MergedApi) mergedApi.apis = make([]string, len(apis)) mergedApi.methods = make(map[string]EthereumApi) @@ -24,7 +24,7 @@ func newMergedApi(apis ...EthereumApi) *mergedApi { } // Supported RPC methods -func (self *mergedApi) Methods() []string { +func (self *MergedApi) Methods() []string { all := make([]string, len(self.methods)) for method, _ := range self.methods { all = append(all, method) @@ -33,7 +33,7 @@ func (self *mergedApi) Methods() []string { } // Call the correct API's Execute method for the given request -func (self *mergedApi) Execute(req *shared.Request) (interface{}, error) { +func (self *MergedApi) Execute(req *shared.Request) (interface{}, error) { if res, _ := self.handle(req); res != nil { return res, nil } @@ -43,11 +43,11 @@ func (self *mergedApi) Execute(req *shared.Request) (interface{}, error) { return nil, shared.NewNotImplementedError(req.Method) } -func (self *mergedApi) Name() string { +func (self *MergedApi) Name() string { return MergedApiName } -func (self *mergedApi) handle(req *shared.Request) (interface{}, error) { +func (self *MergedApi) handle(req *shared.Request) (interface{}, error) { if req.Method == "support_apis" { // provided API's return self.apis, nil } diff --git a/rpc/api/miner.go b/rpc/api/miner.go index b22c4b7ad..496269304 100644 --- a/rpc/api/miner.go +++ b/rpc/api/miner.go @@ -15,30 +15,30 @@ const ( var ( // mapping between methods and handlers MinerMapping = map[string]minerhandler{ - "miner_hashrate": (*miner).Hashrate, - "miner_makeDAG": (*miner).MakeDAG, - "miner_setExtra": (*miner).SetExtra, - "miner_setGasPrice": (*miner).SetGasPrice, - "miner_startAutoDAG": (*miner).StartAutoDAG, - "miner_start": (*miner).StartMiner, - "miner_stopAutoDAG": (*miner).StopAutoDAG, - "miner_stop": (*miner).StopMiner, + "miner_hashrate": (*minerApi).Hashrate, + "miner_makeDAG": (*minerApi).MakeDAG, + "miner_setExtra": (*minerApi).SetExtra, + "miner_setGasPrice": (*minerApi).SetGasPrice, + "miner_startAutoDAG": (*minerApi).StartAutoDAG, + "miner_start": (*minerApi).StartMiner, + "miner_stopAutoDAG": (*minerApi).StopAutoDAG, + "miner_stop": (*minerApi).StopMiner, } ) // miner callback handler -type minerhandler func(*miner, *shared.Request) (interface{}, error) +type minerhandler func(*minerApi, *shared.Request) (interface{}, error) // miner api provider -type miner struct { +type minerApi struct { ethereum *eth.Ethereum methods map[string]minerhandler codec codec.ApiCoder } // create a new miner api instance -func NewMinerApi(ethereum *eth.Ethereum, coder codec.Codec) *miner { - return &miner{ +func NewMinerApi(ethereum *eth.Ethereum, coder codec.Codec) *minerApi { + return &minerApi{ ethereum: ethereum, methods: MinerMapping, codec: coder.New(nil), @@ -46,7 +46,7 @@ func NewMinerApi(ethereum *eth.Ethereum, coder codec.Codec) *miner { } // Execute given request -func (self *miner) Execute(req *shared.Request) (interface{}, error) { +func (self *minerApi) Execute(req *shared.Request) (interface{}, error) { if callback, ok := self.methods[req.Method]; ok { return callback(self, req) } @@ -55,7 +55,7 @@ func (self *miner) Execute(req *shared.Request) (interface{}, error) { } // collection with supported methods -func (self *miner) Methods() []string { +func (self *minerApi) Methods() []string { methods := make([]string, len(self.methods)) i := 0 for k := range self.methods { @@ -65,11 +65,11 @@ func (self *miner) Methods() []string { return methods } -func (self *miner) Name() string { +func (self *minerApi) Name() string { return MinerApiName } -func (self *miner) StartMiner(req *shared.Request) (interface{}, error) { +func (self *minerApi) StartMiner(req *shared.Request) (interface{}, error) { args := new(StartMinerArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, err @@ -87,16 +87,16 @@ func (self *miner) StartMiner(req *shared.Request) (interface{}, error) { return false, err } -func (self *miner) StopMiner(req *shared.Request) (interface{}, error) { +func (self *minerApi) StopMiner(req *shared.Request) (interface{}, error) { self.ethereum.StopMining() return true, nil } -func (self *miner) Hashrate(req *shared.Request) (interface{}, error) { +func (self *minerApi) Hashrate(req *shared.Request) (interface{}, error) { return self.ethereum.Miner().HashRate(), nil } -func (self *miner) SetExtra(req *shared.Request) (interface{}, error) { +func (self *minerApi) SetExtra(req *shared.Request) (interface{}, error) { args := new(SetExtraArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, err @@ -105,7 +105,7 @@ func (self *miner) SetExtra(req *shared.Request) (interface{}, error) { return true, nil } -func (self *miner) SetGasPrice(req *shared.Request) (interface{}, error) { +func (self *minerApi) SetGasPrice(req *shared.Request) (interface{}, error) { args := new(GasPriceArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return false, err @@ -115,17 +115,17 @@ func (self *miner) SetGasPrice(req *shared.Request) (interface{}, error) { return true, nil } -func (self *miner) StartAutoDAG(req *shared.Request) (interface{}, error) { +func (self *minerApi) StartAutoDAG(req *shared.Request) (interface{}, error) { self.ethereum.StartAutoDAG() return true, nil } -func (self *miner) StopAutoDAG(req *shared.Request) (interface{}, error) { +func (self *minerApi) StopAutoDAG(req *shared.Request) (interface{}, error) { self.ethereum.StopAutoDAG() return true, nil } -func (self *miner) MakeDAG(req *shared.Request) (interface{}, error) { +func (self *minerApi) MakeDAG(req *shared.Request) (interface{}, error) { args := new(MakeDAGArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, err diff --git a/rpc/api/net.go b/rpc/api/net.go index 6799d68f6..efc400785 100644 --- a/rpc/api/net.go +++ b/rpc/api/net.go @@ -10,18 +10,18 @@ import ( var ( // mapping between methods and handlers netMapping = map[string]nethandler{ - "net_id": (*net).NetworkVersion, - "net_peerCount": (*net).PeerCount, - "net_listening": (*net).IsListening, - "net_peers": (*net).Peers, + "net_id": (*netApi).NetworkVersion, + "net_peerCount": (*netApi).PeerCount, + "net_listening": (*netApi).IsListening, + "net_peers": (*netApi).Peers, } ) // net callback handler -type nethandler func(*net, *shared.Request) (interface{}, error) +type nethandler func(*netApi, *shared.Request) (interface{}, error) // net api provider -type net struct { +type netApi struct { xeth *xeth.XEth ethereum *eth.Ethereum methods map[string]nethandler @@ -29,8 +29,8 @@ type net struct { } // create a new net api instance -func NewNetApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *net { - return &net{ +func NewNetApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *netApi { + return &netApi{ xeth: xeth, ethereum: eth, methods: netMapping, @@ -39,7 +39,7 @@ func NewNetApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *net { } // collection with supported methods -func (self *net) Methods() []string { +func (self *netApi) Methods() []string { methods := make([]string, len(self.methods)) i := 0 for k := range self.methods { @@ -50,7 +50,7 @@ func (self *net) Methods() []string { } // Execute given request -func (self *net) Execute(req *shared.Request) (interface{}, error) { +func (self *netApi) Execute(req *shared.Request) (interface{}, error) { if callback, ok := self.methods[req.Method]; ok { return callback(self, req) } @@ -58,24 +58,24 @@ func (self *net) Execute(req *shared.Request) (interface{}, error) { return nil, shared.NewNotImplementedError(req.Method) } -func (self *net) Name() string { +func (self *netApi) Name() string { return NetApiName } // Network version -func (self *net) NetworkVersion(req *shared.Request) (interface{}, error) { +func (self *netApi) NetworkVersion(req *shared.Request) (interface{}, error) { return self.xeth.NetworkVersion(), nil } // Number of connected peers -func (self *net) PeerCount(req *shared.Request) (interface{}, error) { +func (self *netApi) PeerCount(req *shared.Request) (interface{}, error) { return self.xeth.PeerCount(), nil } -func (self *net) IsListening(req *shared.Request) (interface{}, error) { +func (self *netApi) IsListening(req *shared.Request) (interface{}, error) { return self.xeth.IsListening(), nil } -func (self *net) Peers(req *shared.Request) (interface{}, error) { +func (self *netApi) Peers(req *shared.Request) (interface{}, error) { return self.ethereum.PeersInfo(), nil } diff --git a/rpc/api/net_js.go b/rpc/api/net_js.go index 6ba0624d8..2fae69c58 100644 --- a/rpc/api/net_js.go +++ b/rpc/api/net_js.go @@ -5,6 +5,13 @@ web3.extend({ property: 'network', methods: [ + new web3.extend.Method({ + name: 'addPeer', + call: 'net_addPeer', + params: 1, + inputFormatter: [web3.extend.utils.formatInputString], + outputFormatter: web3.extend.formatters.formatOutputBool + }), new web3.extend.Method({ name: 'id', call: 'net_id', diff --git a/rpc/api/personal.go b/rpc/api/personal.go index d00363627..08dc4bff5 100644 --- a/rpc/api/personal.go +++ b/rpc/api/personal.go @@ -13,18 +13,18 @@ import ( var ( // mapping between methods and handlers personalMapping = map[string]personalhandler{ - "personal_listAccounts": (*personal).ListAccounts, - "personal_newAccount": (*personal).NewAccount, - "personal_deleteAccount": (*personal).DeleteAccount, - "personal_unlockAccount": (*personal).UnlockAccount, + "personal_listAccounts": (*personalApi).ListAccounts, + "personal_newAccount": (*personalApi).NewAccount, + "personal_deleteAccount": (*personalApi).DeleteAccount, + "personal_unlockAccount": (*personalApi).UnlockAccount, } ) // net callback handler -type personalhandler func(*personal, *shared.Request) (interface{}, error) +type personalhandler func(*personalApi, *shared.Request) (interface{}, error) // net api provider -type personal struct { +type personalApi struct { xeth *xeth.XEth ethereum *eth.Ethereum methods map[string]personalhandler @@ -32,8 +32,8 @@ type personal struct { } // create a new net api instance -func NewPersonal(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *personal { - return &personal{ +func NewPersonalApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *personalApi { + return &personalApi{ xeth: xeth, ethereum: eth, methods: personalMapping, @@ -42,7 +42,7 @@ func NewPersonal(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *persona } // collection with supported methods -func (self *personal) Methods() []string { +func (self *personalApi) Methods() []string { methods := make([]string, len(self.methods)) i := 0 for k := range self.methods { @@ -53,7 +53,7 @@ func (self *personal) Methods() []string { } // Execute given request -func (self *personal) Execute(req *shared.Request) (interface{}, error) { +func (self *personalApi) Execute(req *shared.Request) (interface{}, error) { if callback, ok := self.methods[req.Method]; ok { return callback(self, req) } @@ -61,15 +61,15 @@ func (self *personal) Execute(req *shared.Request) (interface{}, error) { return nil, shared.NewNotImplementedError(req.Method) } -func (self *personal) Name() string { +func (self *personalApi) Name() string { return PersonalApiName } -func (self *personal) ListAccounts(req *shared.Request) (interface{}, error) { +func (self *personalApi) ListAccounts(req *shared.Request) (interface{}, error) { return self.xeth.Accounts(), nil } -func (self *personal) NewAccount(req *shared.Request) (interface{}, error) { +func (self *personalApi) NewAccount(req *shared.Request) (interface{}, error) { args := new(NewAccountArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -80,7 +80,7 @@ func (self *personal) NewAccount(req *shared.Request) (interface{}, error) { return acc.Address.Hex(), err } -func (self *personal) DeleteAccount(req *shared.Request) (interface{}, error) { +func (self *personalApi) DeleteAccount(req *shared.Request) (interface{}, error) { args := new(DeleteAccountArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -95,7 +95,7 @@ func (self *personal) DeleteAccount(req *shared.Request) (interface{}, error) { } } -func (self *personal) UnlockAccount(req *shared.Request) (interface{}, error) { +func (self *personalApi) UnlockAccount(req *shared.Request) (interface{}, error) { args := new(UnlockAccountArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) diff --git a/rpc/api/utils.go b/rpc/api/utils.go index eae23d351..072abf883 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -21,6 +21,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. for i, name := range names { switch strings.ToLower(strings.TrimSpace(name)) { + case AdminApiName: + apis[i] = NewAdminApi(xeth, eth, codec) case DebugApiName: apis[i] = NewDebugApi(xeth, eth, codec) case EthApiName: @@ -30,9 +32,9 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. case NetApiName: apis[i] = NewNetApi(xeth, eth, codec) case PersonalApiName: - apis[i] = NewPersonal(xeth, eth, codec) + apis[i] = NewPersonalApi(xeth, eth, codec) case Web3ApiName: - apis[i] = NewWeb3(xeth, codec) + apis[i] = NewWeb3Api(xeth, codec) default: return nil, fmt.Errorf("Unknown API '%s'", name) } @@ -43,6 +45,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. func Javascript(name string) string { switch strings.ToLower(strings.TrimSpace(name)) { + case AdminApiName: + return Admin_JS case DebugApiName: return Debug_JS case MinerApiName: diff --git a/rpc/api/web3.go b/rpc/api/web3.go index c46457ce6..42b0b7fd9 100644 --- a/rpc/api/web3.go +++ b/rpc/api/web3.go @@ -15,24 +15,24 @@ const ( var ( // mapping between methods and handlers Web3Mapping = map[string]web3handler{ - "web3_sha3": (*web3).Sha3, - "web3_clientVersion": (*web3).ClientVersion, + "web3_sha3": (*web3Api).Sha3, + "web3_clientVersion": (*web3Api).ClientVersion, } ) // web3 callback handler -type web3handler func(*web3, *shared.Request) (interface{}, error) +type web3handler func(*web3Api, *shared.Request) (interface{}, error) // web3 api provider -type web3 struct { +type web3Api struct { xeth *xeth.XEth methods map[string]web3handler codec codec.ApiCoder } // create a new web3 api instance -func NewWeb3(xeth *xeth.XEth, coder codec.Codec) *web3 { - return &web3{ +func NewWeb3Api(xeth *xeth.XEth, coder codec.Codec) *web3Api { + return &web3Api{ xeth: xeth, methods: Web3Mapping, codec: coder.New(nil), @@ -40,7 +40,7 @@ func NewWeb3(xeth *xeth.XEth, coder codec.Codec) *web3 { } // collection with supported methods -func (self *web3) Methods() []string { +func (self *web3Api) Methods() []string { methods := make([]string, len(self.methods)) i := 0 for k := range self.methods { @@ -51,7 +51,7 @@ func (self *web3) Methods() []string { } // Execute given request -func (self *web3) Execute(req *shared.Request) (interface{}, error) { +func (self *web3Api) Execute(req *shared.Request) (interface{}, error) { if callback, ok := self.methods[req.Method]; ok { return callback(self, req) } @@ -59,17 +59,17 @@ func (self *web3) Execute(req *shared.Request) (interface{}, error) { return nil, &shared.NotImplementedError{req.Method} } -func (self *web3) Name() string { +func (self *web3Api) Name() string { return Web3ApiName } // Version of the API this instance provides -func (self *web3) Version() string { +func (self *web3Api) Version() string { return Web3Version } // Calculates the sha3 over req.Params.Data -func (self *web3) Sha3(req *shared.Request) (interface{}, error) { +func (self *web3Api) Sha3(req *shared.Request) (interface{}, error) { args := new(Sha3Args) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, err @@ -79,6 +79,6 @@ func (self *web3) Sha3(req *shared.Request) (interface{}, error) { } // returns the xeth client vrsion -func (self *web3) ClientVersion(req *shared.Request) (interface{}, error) { +func (self *web3Api) ClientVersion(req *shared.Request) (interface{}, error) { return self.xeth.ClientVersion(), nil } -- cgit v1.2.3 From 5f8e5a487588bab49fb3c9321fd8903132426c47 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Wed, 10 Jun 2015 09:42:14 +0200 Subject: upgrade web3.js with _extend support --- rpc/api/admin_js.go | 38 +++++++++++++++++++------------------- rpc/api/debug_js.go | 30 +++++++++++++++--------------- rpc/api/miner_js.go | 48 ++++++++++++++++++++++++------------------------ rpc/api/net_js.go | 26 +++++++++++++------------- rpc/api/personal_js.go | 16 ++++++++-------- 5 files changed, 79 insertions(+), 79 deletions(-) (limited to 'rpc') diff --git a/rpc/api/admin_js.go b/rpc/api/admin_js.go index 02a0e93e1..6255a6c7b 100644 --- a/rpc/api/admin_js.go +++ b/rpc/api/admin_js.go @@ -1,66 +1,66 @@ package api const Admin_JS = ` -web3.extend({ +web3._extend({ property: 'admin', methods: [ - new web3.extend.Method({ + new web3._extend.Method({ name: 'addPeer', call: 'admin_addPeer', params: 1, - inputFormatter: [web3.extend.utils.formatInputString], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'peers', call: 'admin_peers', params: 0, inputFormatter: [], outputFormatter: function(obj) { return obj; } }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'exportChain', call: 'admin_exportChain', params: 1, - inputFormatter: [web3.extend.utils.formatInputString], + inputFormatter: [web3._extend.utils.formatInputString], outputFormatter: function(obj) { return obj; } }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'importChain', call: 'admin_importChain', params: 1, - inputFormatter: [web3.extend.utils.formatInputString], + inputFormatter: [web3._extend.utils.formatInputString], outputFormatter: function(obj) { return obj; } }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'verbosity', call: 'admin_verbosity', params: 1, - inputFormatter: [web3.extend.utils.formatInputInt], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.utils.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'syncStatus', call: 'admin_syncStatus', params: 1, - inputFormatter: [web3.extend.utils.formatInputInt], + inputFormatter: [web3._extend.utils.formatInputInt], outputFormatter: function(obj) { return obj; } }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'setSolc', call: 'admin_setSolc', params: 1, - inputFormatter: [web3.extend.utils.formatInputString], - outputFormatter: web3.extend.formatters.formatOutputString + inputFormatter: [web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputString }) ], properties: [ - new web3.extend.Property({ + new web3._extend.Property({ name: 'nodeInfo', getter: 'admin_nodeInfo', - outputFormatter: web3.extend.formatters.formatOutputString + outputFormatter: web3._extend.formatters.formatOutputString }) ] }); diff --git a/rpc/api/debug_js.go b/rpc/api/debug_js.go index 43c545b2a..fe19a077d 100644 --- a/rpc/api/debug_js.go +++ b/rpc/api/debug_js.go @@ -1,44 +1,44 @@ package api const Debug_JS = ` -web3.extend({ +web3._extend({ property: 'debug', methods: [ - new web3.extend.Method({ + new web3._extend.Method({ name: 'printBlock', call: 'debug_printBlock', params: 1, - inputFormatter: [web3.extend.formatters.formatInputInt], - outputFormatter: web3.extend.formatters.formatOutputString + inputFormatter: [web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputString }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'getBlockRlp', call: 'debug_getBlockRlp', params: 1, - inputFormatter: [web3.extend.formatters.formatInputInt], - outputFormatter: web3.extend.formatters.formatOutputString + inputFormatter: [web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputString }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'setHead', call: 'debug_setHead', params: 1, - inputFormatter: [web3.extend.formatters.formatInputInt], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'processBlock', call: 'debug_processBlock', params: 1, - inputFormatter: [web3.extend.formatters.formatInputInt], + inputFormatter: [web3._extend.formatters.formatInputInt], outputFormatter: function(obj) { return obj; } }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'seedHash', call: 'debug_seedHash', params: 1, - inputFormatter: [web3.extend.formatters.formatInputInt], - outputFormatter: web3.extend.formatters.formatOutputString + inputFormatter: [web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputString }) ], properties: diff --git a/rpc/api/miner_js.go b/rpc/api/miner_js.go index f1c64c5e8..bcf92f6a7 100644 --- a/rpc/api/miner_js.go +++ b/rpc/api/miner_js.go @@ -1,73 +1,73 @@ package api const Miner_JS = ` -web3.extend({ +web3._extend({ property: 'miner', methods: [ - new web3.extend.Method({ + new web3._extend.Method({ name: 'start', call: 'miner_start', params: 1, - inputFormatter: [web3.extend.formatters.formatInputInt], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'stop', call: 'miner_stop', params: 1, - inputFormatter: [web3.extend.formatters.formatInputInt], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'getHashrate', call: 'miner_hashrate', params: 0, inputFormatter: [], - outputFormatter: web3.extend.utils.toDecimal + outputFormatter: web3._extend.utils.toDecimal }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'setExtra', call: 'miner_setExtra', params: 1, - inputFormatter: [web3.extend.utils.formatInputString], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'setGasPrice', call: 'miner_setGasPrice', params: 1, - inputFormatter: [web3.extend.utils.formatInputString], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'startAutoDAG', call: 'miner_startAutoDAG', params: 0, inputFormatter: [], - outputFormatter: web3.extend.formatters.formatOutputBool + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'stopAutoDAG', call: 'miner_stopAutoDAG', params: 0, inputFormatter: [], - outputFormatter: web3.extend.formatters.formatOutputBool + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'makeDAG', call: 'miner_makeDAG', params: 1, - inputFormatter: [web3.extend.formatters.inputDefaultBlockNumberFormatter], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter], + outputFormatter: web3._extend.formatters.formatOutputBool }) ], properties: [ - new web3.extend.Property({ + new web3._extend.Property({ name: 'hashrate', getter: 'miner_hashrate', - outputFormatter: web3.extend.utils.toDecimal + outputFormatter: web3._extend.utils.toDecimal }) ] }); diff --git a/rpc/api/net_js.go b/rpc/api/net_js.go index 2fae69c58..75f6c89f3 100644 --- a/rpc/api/net_js.go +++ b/rpc/api/net_js.go @@ -1,32 +1,32 @@ package api const Net_JS = ` -web3.extend({ +web3._extend({ property: 'network', methods: [ - new web3.extend.Method({ + new web3._extend.Method({ name: 'addPeer', call: 'net_addPeer', params: 1, - inputFormatter: [web3.extend.utils.formatInputString], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'id', call: 'net_id', params: 0, inputFormatter: [], - outputFormatter: web3.extend.formatters.formatOutputString + outputFormatter: web3._extend.formatters.formatOutputString }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'getPeerCount', call: 'net_peerCount', params: 0, inputFormatter: [], - outputFormatter: web3.extend.formatters.formatOutputString + outputFormatter: web3._extend.formatters.formatOutputString }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'peers', call: 'net_peers', params: 0, @@ -36,15 +36,15 @@ web3.extend({ ], properties: [ - new web3.extend.Property({ + new web3._extend.Property({ name: 'listening', getter: 'net_listening', - outputFormatter: web3.extend.formatters.formatOutputBool + outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3.extend.Property({ + new web3._extend.Property({ name: 'peerCount', getter: 'net_peerCount', - outputFormatter: web3.extend.utils.toDecimal + outputFormatter: web3._extend.utils.toDecimal }) ] }); diff --git a/rpc/api/personal_js.go b/rpc/api/personal_js.go index 7fd9a2dea..f9fa60e78 100644 --- a/rpc/api/personal_js.go +++ b/rpc/api/personal_js.go @@ -1,30 +1,30 @@ package api const Personal_JS = ` -web3.extend({ +web3._extend({ property: 'personal', methods: [ - new web3.extend.Method({ + new web3._extend.Method({ name: 'listAccounts', call: 'personal_listAccounts', params: 0, inputFormatter: [], outputFormatter: function(obj) { return obj; } }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'newAccount', call: 'personal_newAccount', params: 1, - inputFormatter: [web3.extend.formatters.formatInputString], - outputFormatter: web3.extend.formatters.formatOutputString + inputFormatter: [web3._extend.formatters.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputString }), - new web3.extend.Method({ + new web3._extend.Method({ name: 'unlockAccount', call: 'personal_unlockAccount', params: 3, - inputFormatter: [web3.extend.formatters.formatInputString,web3.extend.formatters.formatInputString,web3.extend.formatters.formatInputInt], - outputFormatter: web3.extend.formatters.formatOutputBool + inputFormatter: [web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputBool }) ], properties: -- cgit v1.2.3 From 87b62f75a7b9c15c17f3352f1b50ad88966e7070 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Wed, 10 Jun 2015 10:37:10 +0200 Subject: added txpool API --- rpc/api/api.go | 3 ++- rpc/api/txpool.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/txpool_js.go | 18 ++++++++++++++ rpc/api/utils.go | 4 ++++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 rpc/api/txpool.go create mode 100644 rpc/api/txpool_js.go (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index 28b824658..e870ec58e 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -13,6 +13,7 @@ const ( MergedApiName = "merged" MinerApiName = "miner" NetApiName = "net" + txPoolApiName = "txpool" PersonalApiName = "personal" Web3ApiName = "web3" ) @@ -20,7 +21,7 @@ const ( var ( // List with all API's which are offered over the IPC interface by default DefaultIpcApis = strings.Join([]string{ - AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, PersonalApiName, Web3ApiName, + AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, txPoolApiName, PersonalApiName, Web3ApiName, }, ",") ) diff --git a/rpc/api/txpool.go b/rpc/api/txpool.go new file mode 100644 index 000000000..f340c501f --- /dev/null +++ b/rpc/api/txpool.go @@ -0,0 +1,67 @@ +package api + +import ( + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +var ( + // mapping between methods and handlers + txpoolMapping = map[string]txpoolhandler{ + "txpool_status": (*txPoolApi).Status, + } +) + +// net callback handler +type txpoolhandler func(*txPoolApi, *shared.Request) (interface{}, error) + +// txpool api provider +type txPoolApi struct { + xeth *xeth.XEth + ethereum *eth.Ethereum + methods map[string]txpoolhandler + codec codec.ApiCoder +} + +// create a new txpool api instance +func NewTxPoolApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *txPoolApi { + return &txPoolApi{ + xeth: xeth, + ethereum: eth, + methods: txpoolMapping, + codec: coder.New(nil), + } +} + +// collection with supported methods +func (self *txPoolApi) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *txPoolApi) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, shared.NewNotImplementedError(req.Method) +} + +func (self *txPoolApi) Name() string { + return txPoolApiName +} + +func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) { + return map[string]int{ + "pending": self.ethereum.TxPool().GetTransactions().Len(), + "queued": self.ethereum.TxPool().GetQueuedTransactions().Len(), + }, nil +} diff --git a/rpc/api/txpool_js.go b/rpc/api/txpool_js.go new file mode 100644 index 000000000..06528d1c4 --- /dev/null +++ b/rpc/api/txpool_js.go @@ -0,0 +1,18 @@ +package api + +const TxPool_JS = ` +web3._extend({ + property: 'txpool', + methods: + [ + ], + properties: + [ + new web3._extend.Property({ + name: 'status', + getter: 'txpool_status', + outputFormatter: function(obj) { return obj; } + }) + ] +}); +` diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 072abf883..b44a325a8 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -31,6 +31,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. apis[i] = NewMinerApi(eth, codec) case NetApiName: apis[i] = NewNetApi(xeth, eth, codec) + case txPoolApiName: + apis[i] = NewTxPoolApi(xeth, eth, codec) case PersonalApiName: apis[i] = NewPersonalApi(xeth, eth, codec) case Web3ApiName: @@ -53,6 +55,8 @@ func Javascript(name string) string { return Miner_JS case NetApiName: return Net_JS + case txPoolApiName: + return TxPool_JS case PersonalApiName: return Personal_JS } -- cgit v1.2.3 From bd38428f33b127e9c60d26127695e50c55798fcd Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Wed, 10 Jun 2015 11:29:52 +0200 Subject: cleanup of javascript API --- rpc/api/admin.go | 25 +++++++++++++++---------- rpc/api/admin_args.go | 14 ++------------ rpc/api/admin_js.go | 29 +++++++++++++++-------------- rpc/api/miner_args.go | 17 ++++++++++++----- rpc/api/miner_js.go | 7 ------- rpc/api/net.go | 4 ++-- rpc/api/net_js.go | 24 ++++++++++-------------- rpc/api/personal_args.go | 45 ++++++++++++++++++++++++++++----------------- rpc/api/personal_js.go | 12 +++++------- 9 files changed, 89 insertions(+), 88 deletions(-) (limited to 'rpc') diff --git a/rpc/api/admin.go b/rpc/api/admin.go index c37463604..6b89942b2 100644 --- a/rpc/api/admin.go +++ b/rpc/api/admin.go @@ -25,14 +25,15 @@ var ( AdminMapping = map[string]adminhandler{ // "admin_startRPC": (*adminApi).StartRPC, // "admin_stopRPC": (*adminApi).StopRPC, - "admin_addPeer": (*adminApi).AddPeer, - "admin_peers": (*adminApi).Peers, - "admin_nodeInfo": (*adminApi).NodeInfo, - "admin_exportChain": (*adminApi).ExportChain, - "admin_importChain": (*adminApi).ImportChain, - "admin_verbosity": (*adminApi).Verbosity, - "admin_syncStatus": (*adminApi).SyncStatus, - "admin_setSolc": (*adminApi).SetSolc, + "admin_addPeer": (*adminApi).AddPeer, + "admin_peers": (*adminApi).Peers, + "admin_nodeInfo": (*adminApi).NodeInfo, + "admin_exportChain": (*adminApi).ExportChain, + "admin_importChain": (*adminApi).ImportChain, + "admin_verbosity": (*adminApi).Verbosity, + "admin_chainSyncStatus": (*adminApi).ChainSyncStatus, + "admin_setSolc": (*adminApi).SetSolc, + "admin_datadir": (*adminApi).DataDir, } ) @@ -129,6 +130,10 @@ func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) { return self.ethereum.NodeInfo(), nil } +func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) { + return self.ethereum.DataDir, nil +} + func hasAllBlocks(chain *core.ChainManager, bs []*types.Block) bool { for _, b := range bs { if !chain.HasBlock(b.Hash()) { @@ -209,9 +214,9 @@ func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) { return true, nil } -func (self *adminApi) SyncStatus(req *shared.Request) (interface{}, error) { +func (self *adminApi) ChainSyncStatus(req *shared.Request) (interface{}, error) { pending, cached := self.ethereum.Downloader().Stats() - return map[string]interface{}{"available": pending, "waitingForImport": cached}, nil + return map[string]interface{}{"blocksAvailable": pending, "blocksWaitingForImport": cached}, nil } func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) { diff --git a/rpc/api/admin_args.go b/rpc/api/admin_args.go index 9c0cbdcb6..56bb57e20 100644 --- a/rpc/api/admin_args.go +++ b/rpc/api/admin_args.go @@ -3,8 +3,6 @@ package api import ( "encoding/json" - "math/big" - "github.com/ethereum/go-ethereum/rpc/shared" ) @@ -68,16 +66,8 @@ func (args *VerbosityArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewDecodeParamError("Expected enode as argument") } - if levelint, ok := obj[0].(int); ok { - args.Level = levelint - } else if levelstr, ok := obj[0].(string); ok { - if !ok { - return shared.NewInvalidTypeError("level", "not a string") - } - level, success := new(big.Int).SetString(levelstr, 0) - if !success { - return shared.NewDecodeParamError("Unable to parse verbosity level") - } + level, err := numString(obj[0]) + if err == nil { args.Level = int(level.Int64()) } diff --git a/rpc/api/admin_js.go b/rpc/api/admin_js.go index 6255a6c7b..c3e713c67 100644 --- a/rpc/api/admin_js.go +++ b/rpc/api/admin_js.go @@ -12,13 +12,6 @@ web3._extend({ inputFormatter: [web3._extend.utils.formatInputString], outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3._extend.Method({ - name: 'peers', - call: 'admin_peers', - params: 0, - inputFormatter: [], - outputFormatter: function(obj) { return obj; } - }), new web3._extend.Method({ name: 'exportChain', call: 'admin_exportChain', @@ -40,13 +33,6 @@ web3._extend({ inputFormatter: [web3._extend.utils.formatInputInt], outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3._extend.Method({ - name: 'syncStatus', - call: 'admin_syncStatus', - params: 1, - inputFormatter: [web3._extend.utils.formatInputInt], - outputFormatter: function(obj) { return obj; } - }), new web3._extend.Method({ name: 'setSolc', call: 'admin_setSolc', @@ -61,6 +47,21 @@ web3._extend({ name: 'nodeInfo', getter: 'admin_nodeInfo', outputFormatter: web3._extend.formatters.formatOutputString + }), + new web3._extend.Property({ + name: 'peers', + getter: 'admin_peers', + outputFormatter: function(obj) { return obj; } + }), + new web3._extend.Property({ + name: 'datadir', + getter: 'admin_datadir', + outputFormatter: web3._extend.formatters.formatOutputString + }), + new web3._extend.Property({ + name: 'chainSyncStatus', + getter: 'admin_chainSyncStatus', + outputFormatter: function(obj) { return obj; } }) ] }); diff --git a/rpc/api/miner_args.go b/rpc/api/miner_args.go index 6b3d16d48..7b0560c16 100644 --- a/rpc/api/miner_args.go +++ b/rpc/api/miner_args.go @@ -41,6 +41,10 @@ func (args *SetExtraArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewDecodeParamError(err.Error()) } + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) + } + extrastr, ok := obj[0].(string) if !ok { return shared.NewInvalidTypeError("Price", "not a string") @@ -60,13 +64,16 @@ func (args *GasPriceArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewDecodeParamError(err.Error()) } - pricestr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("Price", "not a string") + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) } - args.Price = pricestr - return nil + if pricestr, ok := obj[0].(string); ok { + args.Price = pricestr + return nil + } + + return shared.NewInvalidTypeError("Price", "not a string") } type MakeDAGArgs struct { diff --git a/rpc/api/miner_js.go b/rpc/api/miner_js.go index bcf92f6a7..6290368da 100644 --- a/rpc/api/miner_js.go +++ b/rpc/api/miner_js.go @@ -19,13 +19,6 @@ web3._extend({ inputFormatter: [web3._extend.formatters.formatInputInt], outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3._extend.Method({ - name: 'getHashrate', - call: 'miner_hashrate', - params: 0, - inputFormatter: [], - outputFormatter: web3._extend.utils.toDecimal - }), new web3._extend.Method({ name: 'setExtra', call: 'miner_setExtra', diff --git a/rpc/api/net.go b/rpc/api/net.go index efc400785..6f5d55f12 100644 --- a/rpc/api/net.go +++ b/rpc/api/net.go @@ -10,7 +10,7 @@ import ( var ( // mapping between methods and handlers netMapping = map[string]nethandler{ - "net_id": (*netApi).NetworkVersion, + "net_version": (*netApi).Version, "net_peerCount": (*netApi).PeerCount, "net_listening": (*netApi).IsListening, "net_peers": (*netApi).Peers, @@ -63,7 +63,7 @@ func (self *netApi) Name() string { } // Network version -func (self *netApi) NetworkVersion(req *shared.Request) (interface{}, error) { +func (self *netApi) Version(req *shared.Request) (interface{}, error) { return self.xeth.NetworkVersion(), nil } diff --git a/rpc/api/net_js.go b/rpc/api/net_js.go index 75f6c89f3..1677d9fa6 100644 --- a/rpc/api/net_js.go +++ b/rpc/api/net_js.go @@ -12,26 +12,12 @@ web3._extend({ inputFormatter: [web3._extend.utils.formatInputString], outputFormatter: web3._extend.formatters.formatOutputBool }), - new web3._extend.Method({ - name: 'id', - call: 'net_id', - params: 0, - inputFormatter: [], - outputFormatter: web3._extend.formatters.formatOutputString - }), new web3._extend.Method({ name: 'getPeerCount', call: 'net_peerCount', params: 0, inputFormatter: [], outputFormatter: web3._extend.formatters.formatOutputString - }), - new web3._extend.Method({ - name: 'peers', - call: 'net_peers', - params: 0, - inputFormatter: [], - outputFormatter: function(obj) { return obj; } }) ], properties: @@ -45,6 +31,16 @@ web3._extend({ name: 'peerCount', getter: 'net_peerCount', outputFormatter: web3._extend.utils.toDecimal + }), + new web3._extend.Property({ + name: 'peers', + getter: 'net_peers', + outputFormatter: function(obj) { return obj; } + }), + new web3._extend.Property({ + name: 'version', + getter: 'net_version', + outputFormatter: web3._extend.formatters.formatOutputString }) ] }); diff --git a/rpc/api/personal_args.go b/rpc/api/personal_args.go index b41fc06e7..b3e683638 100644 --- a/rpc/api/personal_args.go +++ b/rpc/api/personal_args.go @@ -16,13 +16,16 @@ func (args *NewAccountArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewDecodeParamError(err.Error()) } - passhrase, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("passhrase", "not a string") + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) } - args.Passphrase = passhrase - return nil + if passhrase, ok := obj[0].(string); ok { + args.Passphrase = passhrase + return nil + } + + return shared.NewInvalidTypeError("passhrase", "not a string") } type DeleteAccountArgs struct { @@ -36,17 +39,21 @@ func (args *DeleteAccountArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewDecodeParamError(err.Error()) } - addr, ok := obj[0].(string) - if !ok { + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + if addr, ok := obj[0].(string); ok { + args.Address = addr + } else { return shared.NewInvalidTypeError("address", "not a string") } - args.Address = addr - passhrase, ok := obj[1].(string) - if !ok { + if passhrase, ok := obj[1].(string); ok { + args.Passphrase = passhrase + } else { return shared.NewInvalidTypeError("passhrase", "not a string") } - args.Passphrase = passhrase return nil } @@ -65,17 +72,21 @@ func (args *UnlockAccountArgs) UnmarshalJSON(b []byte) (err error) { args.Duration = -1 - addrstr, ok := obj[0].(string) - if !ok { + if len(obj) < 2 { + return shared.NewInsufficientParamsError(len(obj), 2) + } + + if addrstr, ok := obj[0].(string); ok { + args.Address = addrstr + } else { return shared.NewInvalidTypeError("address", "not a string") } - args.Address = addrstr - passphrasestr, ok := obj[1].(string) - if !ok { + if passphrasestr, ok := obj[1].(string); ok { + args.Passphrase = passphrasestr + } else { return shared.NewInvalidTypeError("passphrase", "not a string") } - args.Passphrase = passphrasestr return nil } diff --git a/rpc/api/personal_js.go b/rpc/api/personal_js.go index f9fa60e78..ddd47f6a4 100644 --- a/rpc/api/personal_js.go +++ b/rpc/api/personal_js.go @@ -5,13 +5,6 @@ web3._extend({ property: 'personal', methods: [ - new web3._extend.Method({ - name: 'listAccounts', - call: 'personal_listAccounts', - params: 0, - inputFormatter: [], - outputFormatter: function(obj) { return obj; } - }), new web3._extend.Method({ name: 'newAccount', call: 'personal_newAccount', @@ -29,6 +22,11 @@ web3._extend({ ], properties: [ + new web3._extend.Property({ + name: 'accounts', + getter: 'personal_listAccounts', + outputFormatter: function(obj) { return obj; } + }) ] }); ` -- cgit v1.2.3 From 7e41d7ac51fdaba1c03ec3f9cb8cc7a7bc3830f4 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Wed, 10 Jun 2015 12:35:12 +0200 Subject: added shh API --- rpc/api/api.go | 6 +- rpc/api/shh.go | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/shh_args.go | 158 ++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/ssh_js.go | 30 +++++++++ rpc/api/txpool.go | 2 +- rpc/api/utils.go | 8 ++- 6 files changed, 370 insertions(+), 5 deletions(-) create mode 100644 rpc/api/shh.go create mode 100644 rpc/api/shh_args.go create mode 100644 rpc/api/ssh_js.go (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index e870ec58e..206647946 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -13,7 +13,8 @@ const ( MergedApiName = "merged" MinerApiName = "miner" NetApiName = "net" - txPoolApiName = "txpool" + ShhApiName = "shh" + TxPoolApiName = "txpool" PersonalApiName = "personal" Web3ApiName = "web3" ) @@ -21,7 +22,8 @@ const ( var ( // List with all API's which are offered over the IPC interface by default DefaultIpcApis = strings.Join([]string{ - AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, txPoolApiName, PersonalApiName, Web3ApiName, + AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, + ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName, }, ",") ) diff --git a/rpc/api/shh.go b/rpc/api/shh.go new file mode 100644 index 000000000..04c53c93e --- /dev/null +++ b/rpc/api/shh.go @@ -0,0 +1,171 @@ +package api + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/xeth" +) + +var ( + // mapping between methods and handlers + shhMapping = map[string]shhhandler{ + "shh_version": (*shhApi).Version, + "shh_post": (*shhApi).Post, + "shh_hasIdentity": (*shhApi).HasIdentity, + "shh_newIdentity": (*shhApi).NewIdentity, + "shh_newFilter": (*shhApi).NewFilter, + "shh_uninstallFilter": (*shhApi).UninstallFilter, + "shh_getFilterChanges": (*shhApi).GetFilterChanges, + } +) + +func newWhisperOfflineError(method string) error { + return shared.NewNotAvailableError(method, "whisper offline") +} + +// net callback handler +type shhhandler func(*shhApi, *shared.Request) (interface{}, error) + +// shh api provider +type shhApi struct { + xeth *xeth.XEth + ethereum *eth.Ethereum + methods map[string]shhhandler + codec codec.ApiCoder +} + +// create a new whisper api instance +func NewShhApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *shhApi { + return &shhApi{ + xeth: xeth, + ethereum: eth, + methods: shhMapping, + codec: coder.New(nil), + } +} + +// collection with supported methods +func (self *shhApi) Methods() []string { + methods := make([]string, len(self.methods)) + i := 0 + for k := range self.methods { + methods[i] = k + i++ + } + return methods +} + +// Execute given request +func (self *shhApi) Execute(req *shared.Request) (interface{}, error) { + if callback, ok := self.methods[req.Method]; ok { + return callback(self, req) + } + + return nil, shared.NewNotImplementedError(req.Method) +} + +func (self *shhApi) Name() string { + return ShhApiName +} + +func (self *shhApi) Version(req *shared.Request) (interface{}, error) { + w := self.xeth.Whisper() + if w == nil { + return nil, newWhisperOfflineError(req.Method) + } + + return w.Version(), nil +} + +func (self *shhApi) Post(req *shared.Request) (interface{}, error) { + w := self.xeth.Whisper() + if w == nil { + return nil, newWhisperOfflineError(req.Method) + } + + args := new(WhisperMessageArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + + err := w.Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl) + if err != nil { + return false, err + } + + return true, nil +} + +func (self *shhApi) HasIdentity(req *shared.Request) (interface{}, error) { + w := self.xeth.Whisper() + if w == nil { + return nil, newWhisperOfflineError(req.Method) + } + + args := new(WhisperIdentityArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + + return w.HasIdentity(args.Identity), nil +} + +func (self *shhApi) NewIdentity(req *shared.Request) (interface{}, error) { + w := self.xeth.Whisper() + if w == nil { + return nil, newWhisperOfflineError(req.Method) + } + + return w.NewIdentity(), nil +} + +func (self *shhApi) NewFilter(req *shared.Request) (interface{}, error) { + args := new(WhisperFilterArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + + id := self.xeth.NewWhisperFilter(args.To, args.From, args.Topics) + return newHexNum(big.NewInt(int64(id)).Bytes()), nil +} + +func (self *shhApi) UninstallFilter(req *shared.Request) (interface{}, error) { + args := new(FilterIdArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + return self.xeth.UninstallWhisperFilter(args.Id), nil +} + +func (self *shhApi) GetFilterChanges(req *shared.Request) (interface{}, error) { + w := self.xeth.Whisper() + if w == nil { + return nil, newWhisperOfflineError(req.Method) + } + + // Retrieve all the new messages arrived since the last request + args := new(FilterIdArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + + return self.xeth.WhisperMessagesChanged(args.Id), nil +} + +func (self *shhApi) GetMessages(req *shared.Request) (interface{}, error) { + w := self.xeth.Whisper() + if w == nil { + return nil, newWhisperOfflineError(req.Method) + } + + // Retrieve all the cached messages matching a specific, existing filter + args := new(FilterIdArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, err + } + + return self.xeth.WhisperMessages(args.Id), nil +} diff --git a/rpc/api/shh_args.go b/rpc/api/shh_args.go new file mode 100644 index 000000000..00abac232 --- /dev/null +++ b/rpc/api/shh_args.go @@ -0,0 +1,158 @@ +package api + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/rpc/shared" +) + +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 shared.NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return shared.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 WhisperIdentityArgs struct { + Identity string +} + +func (args *WhisperIdentityArgs) 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", "not a string") + } + + 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 shared.NewDecodeParamError(err.Error()) + } + if len(obj) < 1 { + return shared.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 shared.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 shared.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 shared.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 shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string") + } + } + default: + return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array") + } + } + args.Topics = topics + } + return nil +} diff --git a/rpc/api/ssh_js.go b/rpc/api/ssh_js.go new file mode 100644 index 000000000..f401f70f1 --- /dev/null +++ b/rpc/api/ssh_js.go @@ -0,0 +1,30 @@ +package api + +const Shh_JS = ` +web3._extend({ + property: 'shh', + methods: + [ + new web3._extend.Method({ + name: 'post', + call: 'shh_post', + params: 6, + inputFormatter: [web3._extend.formatters.formatInputString, + web3._extend.formatters.formatInputString, + web3._extend.formatters.formatInputString, + , + , web3._extend.formatters.formatInputInt + , web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputBool + }), + ], + properties: + [ + new web3._extend.Property({ + name: 'version', + getter: 'shh_version', + outputFormatter: web3._extend.formatters.formatOutputInt + }) + ] +}); +` diff --git a/rpc/api/txpool.go b/rpc/api/txpool.go index f340c501f..ebbe199b1 100644 --- a/rpc/api/txpool.go +++ b/rpc/api/txpool.go @@ -56,7 +56,7 @@ func (self *txPoolApi) Execute(req *shared.Request) (interface{}, error) { } func (self *txPoolApi) Name() string { - return txPoolApiName + return TxPoolApiName } func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) { diff --git a/rpc/api/utils.go b/rpc/api/utils.go index b44a325a8..ad8a97e92 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -31,7 +31,9 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. apis[i] = NewMinerApi(eth, codec) case NetApiName: apis[i] = NewNetApi(xeth, eth, codec) - case txPoolApiName: + case ShhApiName: + apis[i] = NewShhApi(xeth, eth, codec) + case TxPoolApiName: apis[i] = NewTxPoolApi(xeth, eth, codec) case PersonalApiName: apis[i] = NewPersonalApi(xeth, eth, codec) @@ -55,7 +57,9 @@ func Javascript(name string) string { return Miner_JS case NetApiName: return Net_JS - case txPoolApiName: + case ShhApiName: + return Shh_JS + case TxPoolApiName: return TxPool_JS case PersonalApiName: return Personal_JS -- cgit v1.2.3 From 348f1562e29c80cac3c1d13ff255a25ed4ec81c7 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 10:23:54 +0200 Subject: restructured eth rpc API --- rpc/api/api.go | 3 ++ rpc/api/eth.go | 8 ++++ rpc/api/utils.go | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index 206647946..e431e5c1e 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -32,6 +32,9 @@ type EthereumApi interface { // API identifier Name() string + // API version + ApiVersion() string + // Execute the given request and returns the response or an error Execute(*shared.Request) (interface{}, error) diff --git a/rpc/api/eth.go b/rpc/api/eth.go index f27f17f39..a0b9dad86 100644 --- a/rpc/api/eth.go +++ b/rpc/api/eth.go @@ -11,6 +11,10 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) +const ( + EthApiVersion = "1.0" +) + // eth api provider // See https://github.com/ethereum/wiki/wiki/JSON-RPC type ethApi struct { @@ -97,6 +101,10 @@ func (self *ethApi) Name() string { return EthApiName } +func (self *ethApi) ApiVersion() string { + return EthApiVersion +} + func (self *ethApi) Accounts(req *shared.Request) (interface{}, error) { return self.xeth.Accounts(), nil } diff --git a/rpc/api/utils.go b/rpc/api/utils.go index ad8a97e92..318d7c39b 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -10,6 +10,117 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) +var ( + // Mapping between the different methods each api supports + AutoCompletion = map[string][]string{ + "admin": []string{ + "addPeer", + "peers", + "nodeInfo", + "exportChain", + "importChain", + "verbosity", + "chainSyncStatus", + "setSolc", + "datadir", + }, + "debug": []string{ + "dumpBlock", + "getBlockRlp", + "printBlock", + "processBlock", + "seedHash", + "setHead", + }, + "eth": []string{ + "accounts", + "blockNumber", + "getBalance", + "protocolVersion", + "coinbase", + "mining", + "gasPrice", + "getStorage", + "storageAt", + "getStorageAt", + "getTransactionCount", + "getBlockTransactionCountByHash", + "getBlockTransactionCountByNumber", + "getUncleCountByBlockHash", + "getUncleCountByBlockNumber", + "getData", + "getCode", + "sign", + "sendTransaction", + "transact", + "estimateGas", + "call", + "flush", + "getBlockByHash", + "getBlockByNumber", + "getTransactionByHash", + "getTransactionByBlockHashAndIndex", + "getUncleByBlockHashAndIndex", + "getUncleByBlockNumberAndIndex", + "getCompilers", + "compileSolidity", + "newFilter", + "newBlockFilter", + "newPendingTransactionFilter", + "uninstallFilter", + "getFilterChanges", + "getFilterLogs", + "getLogs", + "hashrate", + "getWork", + "submitWork", + }, + "miner": []string{ + "hashrate", + "makeDAG", + "setExtra", + "setGasPrice", + "startAutoDAG", + "start", + "stopAutoDAG", + "stop", + }, + "net": []string{ + "peerCount", + "listening", + }, + "personal": []string{ + "listAccounts", + "newAccount", + "deleteAccount", + "unlockAccount", + }, + "shh": []string{ + "version", + "post", + "hasIdentity", + "newIdentity", + "newFilter", + "uninstallFilter", + "getFilterChanges", + }, + "txpool": []string{ + "status", + }, + "web3": []string{ + "sha3", + "version", + "fromWei", + "toWei", + "toHex", + "toAscii", + "fromAscii", + "toBigNumber", + "isAddress", + }, + } +) + // Parse a comma separated API string to individual api's func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.Ethereum) ([]EthereumApi, error) { if len(strings.TrimSpace(apistr)) == 0 { -- cgit v1.2.3 From bbfa0a3dcb82e704828d1438261ff26c50c4ccc3 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 11:01:02 +0200 Subject: added API/IPC commandline flags --- rpc/api/api.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index e431e5c1e..f62e48772 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -27,6 +27,11 @@ var ( }, ",") ) +const ( + // List with all API's which are offered over the IPC interface by default + DefaultIpcApis = "eth" +) + // Ethereum RPC API interface type EthereumApi interface { // API identifier -- cgit v1.2.3 From 1b59f890955c3658516daa958d0e4732004a78b7 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 12:43:58 +0200 Subject: added console command --- rpc/api/api.go | 5 ----- rpc/api/mergedapi.go | 22 +++++++++++++++------ rpc/api/web3.go | 7 +++---- rpc/jeth.go | 54 +++++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 62 insertions(+), 26 deletions(-) (limited to 'rpc') diff --git a/rpc/api/api.go b/rpc/api/api.go index f62e48772..e431e5c1e 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -27,11 +27,6 @@ var ( }, ",") ) -const ( - // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = "eth" -) - // Ethereum RPC API interface type EthereumApi interface { // API identifier diff --git a/rpc/api/mergedapi.go b/rpc/api/mergedapi.go index dea8d1289..b62477a14 100644 --- a/rpc/api/mergedapi.go +++ b/rpc/api/mergedapi.go @@ -1,21 +1,27 @@ package api -import "github.com/ethereum/go-ethereum/rpc/shared" +import ( + "github.com/ethereum/go-ethereum/rpc/shared" +) + +const ( + MergedApiVersion = "1.0" +) // combines multiple API's type MergedApi struct { - apis []string + apis map[string]string methods map[string]EthereumApi } // create new merged api instance func newMergedApi(apis ...EthereumApi) *MergedApi { mergedApi := new(MergedApi) - mergedApi.apis = make([]string, len(apis)) + mergedApi.apis = make(map[string]string, len(apis)) mergedApi.methods = make(map[string]EthereumApi) - for i, api := range apis { - mergedApi.apis[i] = api.Name() + for _, api := range apis { + mergedApi.apis[api.Name()] = api.ApiVersion() for _, method := range api.Methods() { mergedApi.methods[method] = api } @@ -47,8 +53,12 @@ func (self *MergedApi) Name() string { return MergedApiName } +func (self *MergedApi) ApiVersion() string { + return MergedApiVersion +} + func (self *MergedApi) handle(req *shared.Request) (interface{}, error) { - if req.Method == "support_apis" { // provided API's + if req.Method == "modules" { // provided API's return self.apis, nil } diff --git a/rpc/api/web3.go b/rpc/api/web3.go index 42b0b7fd9..ed5008446 100644 --- a/rpc/api/web3.go +++ b/rpc/api/web3.go @@ -9,7 +9,7 @@ import ( ) const ( - Web3Version = "1.0.0" + Web3ApiVersion = "1.0" ) var ( @@ -63,9 +63,8 @@ func (self *web3Api) Name() string { return Web3ApiName } -// Version of the API this instance provides -func (self *web3Api) Version() string { - return Web3Version +func (self *web3Api) ApiVersion() string { + return Web3ApiVersion } // Calculates the sha3 over req.Params.Data diff --git a/rpc/jeth.go b/rpc/jeth.go index e578775bb..d1b36906f 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -11,6 +11,10 @@ import ( "github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/shared" "github.com/robertkrimen/otto" + "github.com/ethereum/go-ethereum/rpc/comms" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" + "reflect" ) type Jeth struct { @@ -40,6 +44,13 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { return self.err(call, -32700, err.Error(), nil) } + client, err := comms.NewIpcClient(comms.IpcConfig{self.ipcpath}, codec.JSON) + if err != nil { + fmt.Println("Unable to connect to geth.") + return self.err(call, -32603, err.Error(), -1) + } + defer client.Close() + jsonreq, err := json.Marshal(reqif) var reqs []RpcRequest batch := true @@ -54,22 +65,43 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { call.Otto.Run("var ret_response = new Array(response_len);") for i, req := range reqs { - var respif interface{} - err = self.ethApi.GetRequestReply(&req, &respif) + err := client.Send(&req) if err != nil { - fmt.Println("Error response:", err) + fmt.Println("Error send request:", err) return self.err(call, -32603, err.Error(), req.Id) } - call.Otto.Set("ret_jsonrpc", jsonrpcver) - call.Otto.Set("ret_id", req.Id) - res, _ := json.Marshal(respif) + respif, err := client.Recv() + if err != nil { + fmt.Println("Error recv response:", err) + return self.err(call, -32603, err.Error(), req.Id) + } - call.Otto.Set("ret_result", string(res)) - call.Otto.Set("response_idx", i) - response, err = call.Otto.Run(` - ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; - `) + if res, ok := respif.(shared.SuccessResponse); ok { + call.Otto.Set("ret_id", res.Id) + call.Otto.Set("ret_jsonrpc", res.Jsonrpc) + resObj, _ := json.Marshal(res.Result) + call.Otto.Set("ret_result", string(resObj)) + call.Otto.Set("response_idx", i) + + response, err = call.Otto.Run(` + ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; + `) + } else if res, ok := respif.(shared.ErrorResponse); ok { + fmt.Printf("Error: %s (%d)\n", res.Error.Message, res.Error.Code) + + call.Otto.Set("ret_id", res.Id) + call.Otto.Set("ret_jsonrpc", res.Jsonrpc) + call.Otto.Set("ret_error", res.Error) + call.Otto.Set("response_idx", i) + + response, _ = call.Otto.Run(` + ret_response = { jsonrpc: ret_jsonrpc, id: ret_id, error: ret_error }; + `) + return + } else { + fmt.Printf("unexpected response\n", reflect.TypeOf(respif)) + } } if !batch { -- cgit v1.2.3 From 594a34a88d8e66e82f5333b66f83561f0c0c5bd4 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 13:21:24 +0200 Subject: changed send methods for backwards compatability in geth console --- rpc/jeth.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'rpc') diff --git a/rpc/jeth.go b/rpc/jeth.go index d1b36906f..69df4500e 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -44,6 +44,60 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { return self.err(call, -32700, err.Error(), nil) } + jsonreq, err := json.Marshal(reqif) + var reqs []RpcRequest + batch := true + err = json.Unmarshal(jsonreq, &reqs) + if err != nil { + reqs = make([]RpcRequest, 1) + err = json.Unmarshal(jsonreq, &reqs[0]) + batch = false + } + + call.Otto.Set("response_len", len(reqs)) + call.Otto.Run("var ret_response = new Array(response_len);") + + for i, req := range reqs { + var respif interface{} + err = self.ethApi.GetRequestReply(&req, &respif) + 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_id", req.Id) + + res, _ := json.Marshal(respif) + + call.Otto.Set("ret_result", string(res)) + call.Otto.Set("response_idx", i) + response, err = call.Otto.Run(` + ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; + `) + } + + if !batch { + call.Otto.Run("ret_response = ret_response[0];") + } + + if call.Argument(1).IsObject() { + call.Otto.Set("callback", call.Argument(1)) + call.Otto.Run(` + if (Object.prototype.toString.call(callback) == '[object Function]') { + callback(null, ret_response); + } + `) + } + + return +} + +func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) { + reqif, err := call.Argument(0).Export() + if err != nil { + return self.err(call, -32700, err.Error(), nil) + } + client, err := comms.NewIpcClient(comms.IpcConfig{self.ipcpath}, codec.JSON) if err != nil { fmt.Println("Unable to connect to geth.") -- cgit v1.2.3 From 3c93034a32b0413579996f9c8c11a99fa0f00fe6 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 13:50:36 +0200 Subject: fixed windows build problem --- rpc/comms/ipc_windows.go | 702 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 702 insertions(+) (limited to 'rpc') diff --git a/rpc/comms/ipc_windows.go b/rpc/comms/ipc_windows.go index c48dfb7fb..bee781744 100644 --- a/rpc/comms/ipc_windows.go +++ b/rpc/comms/ipc_windows.go @@ -1,3 +1,4 @@ +<<<<<<< HEAD // +build windows package comms @@ -694,3 +695,704 @@ func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { return nil } +======= +// +build windows + +package comms + +import ( + "fmt" + "io" + "net" + "os" + "sync" + "syscall" + "time" + "unsafe" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rpc/api" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") + procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") + procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") + procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") +) + +func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func disconnectNamedPipe(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func waitNamedPipe(name *uint16, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if initialState { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + + +const ( + // openMode + pipe_access_duplex = 0x3 + pipe_access_inbound = 0x1 + pipe_access_outbound = 0x2 + + // openMode write flags + file_flag_first_pipe_instance = 0x00080000 + file_flag_write_through = 0x80000000 + file_flag_overlapped = 0x40000000 + + // openMode ACL flags + write_dac = 0x00040000 + write_owner = 0x00080000 + access_system_security = 0x01000000 + + // pipeMode + pipe_type_byte = 0x0 + pipe_type_message = 0x4 + + // pipeMode read mode flags + pipe_readmode_byte = 0x0 + pipe_readmode_message = 0x2 + + // pipeMode wait mode flags + pipe_wait = 0x0 + pipe_nowait = 0x1 + + // pipeMode remote-client mode flags + pipe_accept_remote_clients = 0x0 + pipe_reject_remote_clients = 0x8 + + pipe_unlimited_instances = 255 + + nmpwait_wait_forever = 0xFFFFFFFF + + // the two not-an-errors below occur if a client connects to the pipe between + // the server's CreateNamedPipe and ConnectNamedPipe calls. + error_no_data syscall.Errno = 0xE8 + error_pipe_connected syscall.Errno = 0x217 + error_pipe_busy syscall.Errno = 0xE7 + error_sem_timeout syscall.Errno = 0x79 + + error_bad_pathname syscall.Errno = 0xA1 + error_invalid_name syscall.Errno = 0x7B + + error_io_incomplete syscall.Errno = 0x3e4 +) + +var _ net.Conn = (*PipeConn)(nil) +var _ net.Listener = (*PipeListener)(nil) + +// ErrClosed is the error returned by PipeListener.Accept when Close is called +// on the PipeListener. +var ErrClosed = PipeError{"Pipe has been closed.", false} + +// PipeError is an error related to a call to a pipe +type PipeError struct { + msg string + timeout bool +} + +// Error implements the error interface +func (e PipeError) Error() string { + return e.msg +} + +// Timeout implements net.AddrError.Timeout() +func (e PipeError) Timeout() bool { + return e.timeout +} + +// Temporary implements net.AddrError.Temporary() +func (e PipeError) Temporary() bool { + return false +} + +// Dial connects to a named pipe with the given address. If the specified pipe is not available, +// it will wait indefinitely for the pipe to become available. +// +// The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ +// for remote pipes. +// +// Dial will return a PipeError if you pass in a badly formatted pipe name. +// +// Examples: +// // local pipe +// conn, err := Dial(`\\.\pipe\mypipename`) +// +// // remote pipe +// conn, err := Dial(`\\othercomp\pipe\mypipename`) +func Dial(address string) (*PipeConn, error) { + for { + conn, err := dial(address, nmpwait_wait_forever) + if err == nil { + return conn, nil + } + if isPipeNotReady(err) { + <-time.After(100 * time.Millisecond) + continue + } + return nil, err + } +} + +// DialTimeout acts like Dial, but will time out after the duration of timeout +func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) { + deadline := time.Now().Add(timeout) + + now := time.Now() + for now.Before(deadline) { + millis := uint32(deadline.Sub(now) / time.Millisecond) + conn, err := dial(address, millis) + if err == nil { + return conn, nil + } + if err == error_sem_timeout { + // This is WaitNamedPipe's timeout error, so we know we're done + return nil, PipeError{fmt.Sprintf( + "Timed out waiting for pipe '%s' to come available", address), true} + } + if isPipeNotReady(err) { + left := deadline.Sub(time.Now()) + retry := 100 * time.Millisecond + if left > retry { + <-time.After(retry) + } else { + <-time.After(left - time.Millisecond) + } + now = time.Now() + continue + } + return nil, err + } + return nil, PipeError{fmt.Sprintf( + "Timed out waiting for pipe '%s' to come available", address), true} +} + +// isPipeNotReady checks the error to see if it indicates the pipe is not ready +func isPipeNotReady(err error) bool { + // Pipe Busy means another client just grabbed the open pipe end, + // and the server hasn't made a new one yet. + // File Not Found means the server hasn't created the pipe yet. + // Neither is a fatal error. + + return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy +} + +// newOverlapped creates a structure used to track asynchronous +// I/O requests that have been issued. +func newOverlapped() (*syscall.Overlapped, error) { + event, err := createEvent(nil, true, true, nil) + if err != nil { + return nil, err + } + return &syscall.Overlapped{HEvent: event}, nil +} + +// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete. +// This function returns the number of bytes transferred by the operation and an error code if +// applicable (nil otherwise). +func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) { + _, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE) + if err != nil { + return 0, err + } + var transferred uint32 + err = getOverlappedResult(handle, overlapped, &transferred, true) + return transferred, err +} + +// dial is a helper to initiate a connection to a named pipe that has been started by a server. +// The timeout is only enforced if the pipe server has already created the pipe, otherwise +// this function will return immediately. +func dial(address string, timeout uint32) (*PipeConn, error) { + name, err := syscall.UTF16PtrFromString(string(address)) + if err != nil { + return nil, err + } + // If at least one instance of the pipe has been created, this function + // will wait timeout milliseconds for it to become available. + // It will return immediately regardless of timeout, if no instances + // of the named pipe have been created yet. + // If this returns with no error, there is a pipe available. + if err := waitNamedPipe(name, timeout); err != nil { + if err == error_bad_pathname { + // badly formatted pipe name + return nil, badAddr(address) + } + return nil, err + } + pathp, err := syscall.UTF16PtrFromString(address) + if err != nil { + return nil, err + } + handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, + uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, + syscall.FILE_FLAG_OVERLAPPED, 0) + if err != nil { + return nil, err + } + return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil +} + +// Listen returns a new PipeListener that will listen on a pipe with the given +// address. The address must be of the form \\.\pipe\ +// +// Listen will return a PipeError for an incorrectly formatted pipe name. +func Listen(address string) (*PipeListener, error) { + handle, err := createPipe(address, true) + if err == error_invalid_name { + return nil, badAddr(address) + } + if err != nil { + return nil, err + } + return &PipeListener{ + addr: PipeAddr(address), + handle: handle, + }, nil +} + +// PipeListener is a named pipe listener. Clients should typically +// use variables of type net.Listener instead of assuming named pipe. +type PipeListener struct { + addr PipeAddr + handle syscall.Handle + closed bool + + // acceptHandle contains the current handle waiting for + // an incoming connection or nil. + acceptHandle syscall.Handle + // acceptOverlapped is set before waiting on a connection. + // If not waiting, it is nil. + acceptOverlapped *syscall.Overlapped + // acceptMutex protects the handle and overlapped structure. + acceptMutex sync.Mutex +} + +// Accept implements the Accept method in the net.Listener interface; it +// waits for the next call and returns a generic net.Conn. +func (l *PipeListener) Accept() (net.Conn, error) { + c, err := l.AcceptPipe() + for err == error_no_data { + // Ignore clients that connect and immediately disconnect. + c, err = l.AcceptPipe() + } + if err != nil { + return nil, err + } + return c, nil +} + +// AcceptPipe accepts the next incoming call and returns the new connection. +// It might return an error if a client connected and immediately cancelled +// the connection. +func (l *PipeListener) AcceptPipe() (*PipeConn, error) { + if l == nil || l.addr == "" || l.closed { + return nil, syscall.EINVAL + } + + // the first time we call accept, the handle will have been created by the Listen + // call. This is to prevent race conditions where the client thinks the server + // isn't listening because it hasn't actually called create yet. After the first time, we'll + // have to create a new handle each time + handle := l.handle + if handle == 0 { + var err error + handle, err = createPipe(string(l.addr), false) + if err != nil { + return nil, err + } + } else { + l.handle = 0 + } + + overlapped, err := newOverlapped() + if err != nil { + return nil, err + } + defer syscall.CloseHandle(overlapped.HEvent) + if err := connectNamedPipe(handle, overlapped); err != nil && err != error_pipe_connected { + if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING { + l.acceptMutex.Lock() + l.acceptOverlapped = overlapped + l.acceptHandle = handle + l.acceptMutex.Unlock() + defer func() { + l.acceptMutex.Lock() + l.acceptOverlapped = nil + l.acceptHandle = 0 + l.acceptMutex.Unlock() + }() + + _, err = waitForCompletion(handle, overlapped) + } + if err == syscall.ERROR_OPERATION_ABORTED { + // Return error compatible to net.Listener.Accept() in case the + // listener was closed. + return nil, ErrClosed + } + if err != nil { + return nil, err + } + } + return &PipeConn{handle: handle, addr: l.addr}, nil +} + +// Close stops listening on the address. +// Already Accepted connections are not closed. +func (l *PipeListener) Close() error { + if l.closed { + return nil + } + l.closed = true + if l.handle != 0 { + err := disconnectNamedPipe(l.handle) + if err != nil { + return err + } + err = syscall.CloseHandle(l.handle) + if err != nil { + return err + } + l.handle = 0 + } + l.acceptMutex.Lock() + defer l.acceptMutex.Unlock() + if l.acceptOverlapped != nil && l.acceptHandle != 0 { + // Cancel the pending IO. This call does not block, so it is safe + // to hold onto the mutex above. + if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil { + return err + } + err := syscall.CloseHandle(l.acceptOverlapped.HEvent) + if err != nil { + return err + } + l.acceptOverlapped.HEvent = 0 + err = syscall.CloseHandle(l.acceptHandle) + if err != nil { + return err + } + l.acceptHandle = 0 + } + return nil +} + +// Addr returns the listener's network address, a PipeAddr. +func (l *PipeListener) Addr() net.Addr { return l.addr } + +// PipeConn is the implementation of the net.Conn interface for named pipe connections. +type PipeConn struct { + handle syscall.Handle + addr PipeAddr + + // these aren't actually used yet + readDeadline *time.Time + writeDeadline *time.Time +} + +type iodata struct { + n uint32 + err error +} + +// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to +// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending, +// the content of iodata is returned. +func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { + if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { + var timer <-chan time.Time + if deadline != nil { + if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { + timer = time.After(timeDiff) + } + } + done := make(chan iodata) + go func() { + n, err := waitForCompletion(c.handle, overlapped) + done <- iodata{n, err} + }() + select { + case data = <-done: + case <-timer: + syscall.CancelIoEx(c.handle, overlapped) + data = iodata{0, timeout(c.addr.String())} + } + } + // Windows will produce ERROR_BROKEN_PIPE upon closing + // a handle on the other end of a connection. Go RPC + // expects an io.EOF error in this case. + if data.err == syscall.ERROR_BROKEN_PIPE { + data.err = io.EOF + } + return int(data.n), data.err +} + +// Read implements the net.Conn Read method. +func (c *PipeConn) Read(b []byte) (int, error) { + // Use ReadFile() rather than Read() because the latter + // contains a workaround that eats ERROR_BROKEN_PIPE. + overlapped, err := newOverlapped() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(overlapped.HEvent) + var n uint32 + err = syscall.ReadFile(c.handle, b, &n, overlapped) + return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped) +} + +// Write implements the net.Conn Write method. +func (c *PipeConn) Write(b []byte) (int, error) { + overlapped, err := newOverlapped() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(overlapped.HEvent) + var n uint32 + err = syscall.WriteFile(c.handle, b, &n, overlapped) + return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped) +} + +// Close closes the connection. +func (c *PipeConn) Close() error { + return syscall.CloseHandle(c.handle) +} + +// LocalAddr returns the local network address. +func (c *PipeConn) LocalAddr() net.Addr { + return c.addr +} + +// RemoteAddr returns the remote network address. +func (c *PipeConn) RemoteAddr() net.Addr { + // not sure what to do here, we don't have remote addr.... + return c.addr +} + +// SetDeadline implements the net.Conn SetDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetDeadline(t time.Time) error { + c.SetReadDeadline(t) + c.SetWriteDeadline(t) + return nil +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetReadDeadline(t time.Time) error { + c.readDeadline = &t + return nil +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetWriteDeadline(t time.Time) error { + c.writeDeadline = &t + return nil +} + +// PipeAddr represents the address of a named pipe. +type PipeAddr string + +// Network returns the address's network name, "pipe". +func (a PipeAddr) Network() string { return "pipe" } + +// String returns the address of the pipe +func (a PipeAddr) String() string { + return string(a) +} + +// createPipe is a helper function to make sure we always create pipes +// with the same arguments, since subsequent calls to create pipe need +// to use the same arguments as the first one. If first is set, fail +// if the pipe already exists. +func createPipe(address string, first bool) (syscall.Handle, error) { + n, err := syscall.UTF16PtrFromString(address) + if err != nil { + return 0, err + } + mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED) + if first { + mode |= file_flag_first_pipe_instance + } + return createNamedPipe(n, + mode, + pipe_type_byte, + pipe_unlimited_instances, + 512, 512, 0, nil) +} + +func badAddr(addr string) PipeError { + return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false} +} +func timeout(addr string) PipeError { + return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} +} + + + +func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { + c, err := Dial(cfg.Endpoint) + if err != nil { + return nil, err + } + + return &ipcClient{codec.New(c)}, nil +} + +func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { + os.Remove(cfg.Endpoint) // in case it still exists from a previous run + + l, err := Listen(cfg.Endpoint) + if err != nil { + return err + } + os.Chmod(cfg.Endpoint, 0600) + + go func() { + for { + conn, err := l.Accept() + if err != nil { + glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err) + continue + } + + go func(conn net.Conn) { + codec := codec.New(conn) + + for { + req, err := codec.ReadRequest() + if err == io.EOF { + codec.Close() + return + } else if err != nil { + glog.V(logger.Error).Infof("IPC recv err - %v\n", err) + codec.Close() + return + } + + var rpcResponse interface{} + res, err := api.Execute(req) + + rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) + err = codec.WriteResponse(rpcResponse) + if err != nil { + glog.V(logger.Error).Infof("IPC send err - %v\n", err) + codec.Close() + return + } + } + }(conn) + } + }() + + glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) + + return nil +} +>>>>>>> fixed windows build problem -- cgit v1.2.3 From 87dace1fa96ed4426ea963ba4f9559c05bf42e35 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 14:42:15 +0200 Subject: added miner API --- rpc/api/miner.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'rpc') diff --git a/rpc/api/miner.go b/rpc/api/miner.go index 496269304..0b5e74f52 100644 --- a/rpc/api/miner.go +++ b/rpc/api/miner.go @@ -9,7 +9,7 @@ import ( ) const ( - MinerVersion = "1.0.0" + MinerApiVersion = "1.0" ) var ( @@ -69,6 +69,10 @@ func (self *minerApi) Name() string { return MinerApiName } +func (self *minerApi) ApiVersion() string { + return MinerApiVersion +} + func (self *minerApi) StartMiner(req *shared.Request) (interface{}, error) { args := new(StartMinerArgs) if err := self.codec.Decode(req.Params, &args); err != nil { -- cgit v1.2.3 From 1fe617fa5791bffb6b3cc60636c6629c7aca36d3 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Mon, 8 Jun 2015 14:50:11 +0200 Subject: added net API --- rpc/api/net.go | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'rpc') diff --git a/rpc/api/net.go b/rpc/api/net.go index 6f5d55f12..d6888ee4a 100644 --- a/rpc/api/net.go +++ b/rpc/api/net.go @@ -7,6 +7,10 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) +const ( + NetApiVersion = "1.0" +) + var ( // mapping between methods and handlers netMapping = map[string]nethandler{ @@ -62,6 +66,10 @@ func (self *netApi) Name() string { return NetApiName } +func (self *netApi) ApiVersion() string { + return NetApiVersion +} + // Network version func (self *netApi) Version(req *shared.Request) (interface{}, error) { return self.xeth.NetworkVersion(), nil -- cgit v1.2.3 From 7584e68c21cfd155a9e72b29422d8d458691d4ae Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Wed, 10 Jun 2015 09:42:14 +0200 Subject: upgrade web3.js with _extend support --- rpc/api/admin.go | 16 +++++++++++++--- rpc/api/debug.go | 8 ++++++-- rpc/api/debug_js.go | 7 +++++++ rpc/api/personal.go | 8 ++++++++ rpc/api/personal_js.go | 2 +- rpc/api/shh.go | 8 ++++++++ rpc/api/txpool.go | 8 ++++++++ 7 files changed, 51 insertions(+), 6 deletions(-) (limited to 'rpc') diff --git a/rpc/api/admin.go b/rpc/api/admin.go index 6b89942b2..a6b9cf050 100644 --- a/rpc/api/admin.go +++ b/rpc/api/admin.go @@ -16,7 +16,7 @@ import ( ) const ( - AdminVersion = "1.0.0" + AdminApiversion = "1.0" importBatchSize = 2500 ) @@ -82,6 +82,10 @@ func (self *adminApi) Name() string { return AdminApiName } +func (self *adminApi) ApiVersion() string { + return AdminApiversion +} + func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) { args := new(AddPeerArgs) if err := self.codec.Decode(req.Params, &args); err != nil { @@ -215,8 +219,14 @@ func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) { } func (self *adminApi) ChainSyncStatus(req *shared.Request) (interface{}, error) { - pending, cached := self.ethereum.Downloader().Stats() - return map[string]interface{}{"blocksAvailable": pending, "blocksWaitingForImport": cached}, nil + pending, cached, importing, estimate := self.ethereum.Downloader().Stats() + + return map[string]interface{}{ + "blocksAvailable": pending, + "blocksWaitingForImport": cached, + "importing": importing, + "estimate": estimate.String(), + }, nil } func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) { diff --git a/rpc/api/debug.go b/rpc/api/debug.go index 2930ad870..5b6a449dc 100644 --- a/rpc/api/debug.go +++ b/rpc/api/debug.go @@ -14,7 +14,7 @@ import ( ) const ( - DebugVersion = "1.0.0" + DebugApiVersion = "1.0" ) var ( @@ -74,6 +74,10 @@ func (self *debugApi) Name() string { return DebugApiName } +func (self *debugApi) ApiVersion() string { + return DebugApiVersion +} + func (self *debugApi) PrintBlock(req *shared.Request) (interface{}, error) { args := new(BlockNumArg) if err := self.codec.Decode(req.Params, &args); err != nil { @@ -100,7 +104,7 @@ func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) { return nil, nil } - return stateDb.Dump(), nil + return stateDb.RawDump(), nil } func (self *debugApi) GetBlockRlp(req *shared.Request) (interface{}, error) { diff --git a/rpc/api/debug_js.go b/rpc/api/debug_js.go index fe19a077d..35fecb75f 100644 --- a/rpc/api/debug_js.go +++ b/rpc/api/debug_js.go @@ -39,6 +39,13 @@ web3._extend({ params: 1, inputFormatter: [web3._extend.formatters.formatInputInt], outputFormatter: web3._extend.formatters.formatOutputString + }) , + new web3._extend.Method({ + name: 'dumpBlock', + call: 'debug_dumpBlock', + params: 1, + inputFormatter: [web3._extend.formatters.formatInputInt], + outputFormatter: function(obj) { return obj; } }) ], properties: diff --git a/rpc/api/personal.go b/rpc/api/personal.go index 08dc4bff5..7a6c91c82 100644 --- a/rpc/api/personal.go +++ b/rpc/api/personal.go @@ -10,6 +10,10 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) +const ( + PersonalApiVersion = "1.0" +) + var ( // mapping between methods and handlers personalMapping = map[string]personalhandler{ @@ -65,6 +69,10 @@ func (self *personalApi) Name() string { return PersonalApiName } +func (self *personalApi) ApiVersion() string { + return PersonalApiVersion +} + func (self *personalApi) ListAccounts(req *shared.Request) (interface{}, error) { return self.xeth.Accounts(), nil } diff --git a/rpc/api/personal_js.go b/rpc/api/personal_js.go index ddd47f6a4..463a2c7f5 100644 --- a/rpc/api/personal_js.go +++ b/rpc/api/personal_js.go @@ -23,7 +23,7 @@ web3._extend({ properties: [ new web3._extend.Property({ - name: 'accounts', + name: 'listAccounts', getter: 'personal_listAccounts', outputFormatter: function(obj) { return obj; } }) diff --git a/rpc/api/shh.go b/rpc/api/shh.go index 04c53c93e..e83a7b22e 100644 --- a/rpc/api/shh.go +++ b/rpc/api/shh.go @@ -9,6 +9,10 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) +const ( + ShhApiVersion = "1.0" +) + var ( // mapping between methods and handlers shhMapping = map[string]shhhandler{ @@ -71,6 +75,10 @@ func (self *shhApi) Name() string { return ShhApiName } +func (self *shhApi) ApiVersion() string { + return ShhApiVersion +} + func (self *shhApi) Version(req *shared.Request) (interface{}, error) { w := self.xeth.Whisper() if w == nil { diff --git a/rpc/api/txpool.go b/rpc/api/txpool.go index ebbe199b1..64550bdaf 100644 --- a/rpc/api/txpool.go +++ b/rpc/api/txpool.go @@ -7,6 +7,10 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) +const ( + TxPoolApiVersion = "1.0" +) + var ( // mapping between methods and handlers txpoolMapping = map[string]txpoolhandler{ @@ -59,6 +63,10 @@ func (self *txPoolApi) Name() string { return TxPoolApiName } +func (self *txPoolApi) ApiVersion() string { + return TxPoolApiVersion +} + func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) { return map[string]int{ "pending": self.ethereum.TxPool().GetTransactions().Len(), -- cgit v1.2.3 From ec6a7b35f68d4fd0fbf8e59f70096765cff4bffc Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Thu, 11 Jun 2015 15:00:33 +0200 Subject: removed obsolete print statement --- rpc/jeth.go | 86 ------------------------------------------------------------- 1 file changed, 86 deletions(-) (limited to 'rpc') diff --git a/rpc/jeth.go b/rpc/jeth.go index 69df4500e..e578775bb 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -11,10 +11,6 @@ import ( "github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/shared" "github.com/robertkrimen/otto" - "github.com/ethereum/go-ethereum/rpc/comms" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "reflect" ) type Jeth struct { @@ -173,85 +169,3 @@ func (self *Jeth) SendIpc(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 { - return self.err(call, -32700, err.Error(), nil) - } - - client, err := comms.NewIpcClient(comms.IpcConfig{self.ipcpath}, codec.JSON) - if err != nil { - fmt.Println("Unable to connect to geth.") - return self.err(call, -32603, err.Error(), -1) - } - defer client.Close() - - jsonreq, err := json.Marshal(reqif) - var reqs []RpcRequest - batch := true - err = json.Unmarshal(jsonreq, &reqs) - if err != nil { - reqs = make([]RpcRequest, 1) - err = json.Unmarshal(jsonreq, &reqs[0]) - batch = false - } - - call.Otto.Set("response_len", len(reqs)) - call.Otto.Run("var ret_response = new Array(response_len);") - - for i, req := range reqs { - err := client.Send(&req) - if err != nil { - fmt.Println("Error send request:", err) - return self.err(call, -32603, err.Error(), req.Id) - } - - respif, err := client.Recv() - if err != nil { - fmt.Println("Error recv response:", err) - return self.err(call, -32603, err.Error(), req.Id) - } - - if res, ok := respif.(shared.SuccessResponse); ok { - call.Otto.Set("ret_id", res.Id) - call.Otto.Set("ret_jsonrpc", res.Jsonrpc) - resObj, _ := json.Marshal(res.Result) - call.Otto.Set("ret_result", string(resObj)) - call.Otto.Set("response_idx", i) - - response, err = call.Otto.Run(` - ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; - `) - } else if res, ok := respif.(shared.ErrorResponse); ok { - fmt.Printf("Error: %s (%d)\n", res.Error.Message, res.Error.Code) - - call.Otto.Set("ret_id", res.Id) - call.Otto.Set("ret_jsonrpc", res.Jsonrpc) - call.Otto.Set("ret_error", res.Error) - call.Otto.Set("response_idx", i) - - response, _ = call.Otto.Run(` - ret_response = { jsonrpc: ret_jsonrpc, id: ret_id, error: ret_error }; - `) - return - } else { - fmt.Printf("unexpected response\n", reflect.TypeOf(respif)) - } - } - - if !batch { - call.Otto.Run("ret_response = ret_response[0];") - } - - if call.Argument(1).IsObject() { - call.Otto.Set("callback", call.Argument(1)) - call.Otto.Run(` - if (Object.prototype.toString.call(callback) == '[object Function]') { - callback(null, ret_response); - } - `) - } - - return -} -- cgit v1.2.3 From b3c07f167f8b82d1079abe6e15cd1f480712b030 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Thu, 11 Jun 2015 15:06:12 +0200 Subject: fixed incomplete merge action --- rpc/comms/ipc_windows.go | 702 ----------------------------------------------- 1 file changed, 702 deletions(-) (limited to 'rpc') diff --git a/rpc/comms/ipc_windows.go b/rpc/comms/ipc_windows.go index bee781744..c48dfb7fb 100644 --- a/rpc/comms/ipc_windows.go +++ b/rpc/comms/ipc_windows.go @@ -1,4 +1,3 @@ -<<<<<<< HEAD // +build windows package comms @@ -695,704 +694,3 @@ func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { return nil } -======= -// +build windows - -package comms - -import ( - "fmt" - "io" - "net" - "os" - "sync" - "syscall" - "time" - "unsafe" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rpc/api" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -var ( - modkernel32 = syscall.NewLazyDLL("kernel32.dll") - - procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") - procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") - procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") - procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") - procCancelIoEx = modkernel32.NewProc("CancelIoEx") -) - -func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func disconnectNamedPipe(handle syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func waitNamedPipe(name *uint16, timeout uint32) (err error) { - r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { - var _p0 uint32 - if manualReset { - _p0 = 1 - } else { - _p0 = 0 - } - var _p1 uint32 - if initialState { - _p1 = 1 - } else { - _p1 = 0 - } - r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { - var _p0 uint32 - if wait { - _p0 = 1 - } else { - _p0 = 0 - } - r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - - -const ( - // openMode - pipe_access_duplex = 0x3 - pipe_access_inbound = 0x1 - pipe_access_outbound = 0x2 - - // openMode write flags - file_flag_first_pipe_instance = 0x00080000 - file_flag_write_through = 0x80000000 - file_flag_overlapped = 0x40000000 - - // openMode ACL flags - write_dac = 0x00040000 - write_owner = 0x00080000 - access_system_security = 0x01000000 - - // pipeMode - pipe_type_byte = 0x0 - pipe_type_message = 0x4 - - // pipeMode read mode flags - pipe_readmode_byte = 0x0 - pipe_readmode_message = 0x2 - - // pipeMode wait mode flags - pipe_wait = 0x0 - pipe_nowait = 0x1 - - // pipeMode remote-client mode flags - pipe_accept_remote_clients = 0x0 - pipe_reject_remote_clients = 0x8 - - pipe_unlimited_instances = 255 - - nmpwait_wait_forever = 0xFFFFFFFF - - // the two not-an-errors below occur if a client connects to the pipe between - // the server's CreateNamedPipe and ConnectNamedPipe calls. - error_no_data syscall.Errno = 0xE8 - error_pipe_connected syscall.Errno = 0x217 - error_pipe_busy syscall.Errno = 0xE7 - error_sem_timeout syscall.Errno = 0x79 - - error_bad_pathname syscall.Errno = 0xA1 - error_invalid_name syscall.Errno = 0x7B - - error_io_incomplete syscall.Errno = 0x3e4 -) - -var _ net.Conn = (*PipeConn)(nil) -var _ net.Listener = (*PipeListener)(nil) - -// ErrClosed is the error returned by PipeListener.Accept when Close is called -// on the PipeListener. -var ErrClosed = PipeError{"Pipe has been closed.", false} - -// PipeError is an error related to a call to a pipe -type PipeError struct { - msg string - timeout bool -} - -// Error implements the error interface -func (e PipeError) Error() string { - return e.msg -} - -// Timeout implements net.AddrError.Timeout() -func (e PipeError) Timeout() bool { - return e.timeout -} - -// Temporary implements net.AddrError.Temporary() -func (e PipeError) Temporary() bool { - return false -} - -// Dial connects to a named pipe with the given address. If the specified pipe is not available, -// it will wait indefinitely for the pipe to become available. -// -// The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ -// for remote pipes. -// -// Dial will return a PipeError if you pass in a badly formatted pipe name. -// -// Examples: -// // local pipe -// conn, err := Dial(`\\.\pipe\mypipename`) -// -// // remote pipe -// conn, err := Dial(`\\othercomp\pipe\mypipename`) -func Dial(address string) (*PipeConn, error) { - for { - conn, err := dial(address, nmpwait_wait_forever) - if err == nil { - return conn, nil - } - if isPipeNotReady(err) { - <-time.After(100 * time.Millisecond) - continue - } - return nil, err - } -} - -// DialTimeout acts like Dial, but will time out after the duration of timeout -func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) { - deadline := time.Now().Add(timeout) - - now := time.Now() - for now.Before(deadline) { - millis := uint32(deadline.Sub(now) / time.Millisecond) - conn, err := dial(address, millis) - if err == nil { - return conn, nil - } - if err == error_sem_timeout { - // This is WaitNamedPipe's timeout error, so we know we're done - return nil, PipeError{fmt.Sprintf( - "Timed out waiting for pipe '%s' to come available", address), true} - } - if isPipeNotReady(err) { - left := deadline.Sub(time.Now()) - retry := 100 * time.Millisecond - if left > retry { - <-time.After(retry) - } else { - <-time.After(left - time.Millisecond) - } - now = time.Now() - continue - } - return nil, err - } - return nil, PipeError{fmt.Sprintf( - "Timed out waiting for pipe '%s' to come available", address), true} -} - -// isPipeNotReady checks the error to see if it indicates the pipe is not ready -func isPipeNotReady(err error) bool { - // Pipe Busy means another client just grabbed the open pipe end, - // and the server hasn't made a new one yet. - // File Not Found means the server hasn't created the pipe yet. - // Neither is a fatal error. - - return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy -} - -// newOverlapped creates a structure used to track asynchronous -// I/O requests that have been issued. -func newOverlapped() (*syscall.Overlapped, error) { - event, err := createEvent(nil, true, true, nil) - if err != nil { - return nil, err - } - return &syscall.Overlapped{HEvent: event}, nil -} - -// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete. -// This function returns the number of bytes transferred by the operation and an error code if -// applicable (nil otherwise). -func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) { - _, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE) - if err != nil { - return 0, err - } - var transferred uint32 - err = getOverlappedResult(handle, overlapped, &transferred, true) - return transferred, err -} - -// dial is a helper to initiate a connection to a named pipe that has been started by a server. -// The timeout is only enforced if the pipe server has already created the pipe, otherwise -// this function will return immediately. -func dial(address string, timeout uint32) (*PipeConn, error) { - name, err := syscall.UTF16PtrFromString(string(address)) - if err != nil { - return nil, err - } - // If at least one instance of the pipe has been created, this function - // will wait timeout milliseconds for it to become available. - // It will return immediately regardless of timeout, if no instances - // of the named pipe have been created yet. - // If this returns with no error, there is a pipe available. - if err := waitNamedPipe(name, timeout); err != nil { - if err == error_bad_pathname { - // badly formatted pipe name - return nil, badAddr(address) - } - return nil, err - } - pathp, err := syscall.UTF16PtrFromString(address) - if err != nil { - return nil, err - } - handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, - uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, - syscall.FILE_FLAG_OVERLAPPED, 0) - if err != nil { - return nil, err - } - return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil -} - -// Listen returns a new PipeListener that will listen on a pipe with the given -// address. The address must be of the form \\.\pipe\ -// -// Listen will return a PipeError for an incorrectly formatted pipe name. -func Listen(address string) (*PipeListener, error) { - handle, err := createPipe(address, true) - if err == error_invalid_name { - return nil, badAddr(address) - } - if err != nil { - return nil, err - } - return &PipeListener{ - addr: PipeAddr(address), - handle: handle, - }, nil -} - -// PipeListener is a named pipe listener. Clients should typically -// use variables of type net.Listener instead of assuming named pipe. -type PipeListener struct { - addr PipeAddr - handle syscall.Handle - closed bool - - // acceptHandle contains the current handle waiting for - // an incoming connection or nil. - acceptHandle syscall.Handle - // acceptOverlapped is set before waiting on a connection. - // If not waiting, it is nil. - acceptOverlapped *syscall.Overlapped - // acceptMutex protects the handle and overlapped structure. - acceptMutex sync.Mutex -} - -// Accept implements the Accept method in the net.Listener interface; it -// waits for the next call and returns a generic net.Conn. -func (l *PipeListener) Accept() (net.Conn, error) { - c, err := l.AcceptPipe() - for err == error_no_data { - // Ignore clients that connect and immediately disconnect. - c, err = l.AcceptPipe() - } - if err != nil { - return nil, err - } - return c, nil -} - -// AcceptPipe accepts the next incoming call and returns the new connection. -// It might return an error if a client connected and immediately cancelled -// the connection. -func (l *PipeListener) AcceptPipe() (*PipeConn, error) { - if l == nil || l.addr == "" || l.closed { - return nil, syscall.EINVAL - } - - // the first time we call accept, the handle will have been created by the Listen - // call. This is to prevent race conditions where the client thinks the server - // isn't listening because it hasn't actually called create yet. After the first time, we'll - // have to create a new handle each time - handle := l.handle - if handle == 0 { - var err error - handle, err = createPipe(string(l.addr), false) - if err != nil { - return nil, err - } - } else { - l.handle = 0 - } - - overlapped, err := newOverlapped() - if err != nil { - return nil, err - } - defer syscall.CloseHandle(overlapped.HEvent) - if err := connectNamedPipe(handle, overlapped); err != nil && err != error_pipe_connected { - if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING { - l.acceptMutex.Lock() - l.acceptOverlapped = overlapped - l.acceptHandle = handle - l.acceptMutex.Unlock() - defer func() { - l.acceptMutex.Lock() - l.acceptOverlapped = nil - l.acceptHandle = 0 - l.acceptMutex.Unlock() - }() - - _, err = waitForCompletion(handle, overlapped) - } - if err == syscall.ERROR_OPERATION_ABORTED { - // Return error compatible to net.Listener.Accept() in case the - // listener was closed. - return nil, ErrClosed - } - if err != nil { - return nil, err - } - } - return &PipeConn{handle: handle, addr: l.addr}, nil -} - -// Close stops listening on the address. -// Already Accepted connections are not closed. -func (l *PipeListener) Close() error { - if l.closed { - return nil - } - l.closed = true - if l.handle != 0 { - err := disconnectNamedPipe(l.handle) - if err != nil { - return err - } - err = syscall.CloseHandle(l.handle) - if err != nil { - return err - } - l.handle = 0 - } - l.acceptMutex.Lock() - defer l.acceptMutex.Unlock() - if l.acceptOverlapped != nil && l.acceptHandle != 0 { - // Cancel the pending IO. This call does not block, so it is safe - // to hold onto the mutex above. - if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil { - return err - } - err := syscall.CloseHandle(l.acceptOverlapped.HEvent) - if err != nil { - return err - } - l.acceptOverlapped.HEvent = 0 - err = syscall.CloseHandle(l.acceptHandle) - if err != nil { - return err - } - l.acceptHandle = 0 - } - return nil -} - -// Addr returns the listener's network address, a PipeAddr. -func (l *PipeListener) Addr() net.Addr { return l.addr } - -// PipeConn is the implementation of the net.Conn interface for named pipe connections. -type PipeConn struct { - handle syscall.Handle - addr PipeAddr - - // these aren't actually used yet - readDeadline *time.Time - writeDeadline *time.Time -} - -type iodata struct { - n uint32 - err error -} - -// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to -// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending, -// the content of iodata is returned. -func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { - if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { - var timer <-chan time.Time - if deadline != nil { - if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { - timer = time.After(timeDiff) - } - } - done := make(chan iodata) - go func() { - n, err := waitForCompletion(c.handle, overlapped) - done <- iodata{n, err} - }() - select { - case data = <-done: - case <-timer: - syscall.CancelIoEx(c.handle, overlapped) - data = iodata{0, timeout(c.addr.String())} - } - } - // Windows will produce ERROR_BROKEN_PIPE upon closing - // a handle on the other end of a connection. Go RPC - // expects an io.EOF error in this case. - if data.err == syscall.ERROR_BROKEN_PIPE { - data.err = io.EOF - } - return int(data.n), data.err -} - -// Read implements the net.Conn Read method. -func (c *PipeConn) Read(b []byte) (int, error) { - // Use ReadFile() rather than Read() because the latter - // contains a workaround that eats ERROR_BROKEN_PIPE. - overlapped, err := newOverlapped() - if err != nil { - return 0, err - } - defer syscall.CloseHandle(overlapped.HEvent) - var n uint32 - err = syscall.ReadFile(c.handle, b, &n, overlapped) - return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped) -} - -// Write implements the net.Conn Write method. -func (c *PipeConn) Write(b []byte) (int, error) { - overlapped, err := newOverlapped() - if err != nil { - return 0, err - } - defer syscall.CloseHandle(overlapped.HEvent) - var n uint32 - err = syscall.WriteFile(c.handle, b, &n, overlapped) - return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped) -} - -// Close closes the connection. -func (c *PipeConn) Close() error { - return syscall.CloseHandle(c.handle) -} - -// LocalAddr returns the local network address. -func (c *PipeConn) LocalAddr() net.Addr { - return c.addr -} - -// RemoteAddr returns the remote network address. -func (c *PipeConn) RemoteAddr() net.Addr { - // not sure what to do here, we don't have remote addr.... - return c.addr -} - -// SetDeadline implements the net.Conn SetDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetDeadline(t time.Time) error { - c.SetReadDeadline(t) - c.SetWriteDeadline(t) - return nil -} - -// SetReadDeadline implements the net.Conn SetReadDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetReadDeadline(t time.Time) error { - c.readDeadline = &t - return nil -} - -// SetWriteDeadline implements the net.Conn SetWriteDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetWriteDeadline(t time.Time) error { - c.writeDeadline = &t - return nil -} - -// PipeAddr represents the address of a named pipe. -type PipeAddr string - -// Network returns the address's network name, "pipe". -func (a PipeAddr) Network() string { return "pipe" } - -// String returns the address of the pipe -func (a PipeAddr) String() string { - return string(a) -} - -// createPipe is a helper function to make sure we always create pipes -// with the same arguments, since subsequent calls to create pipe need -// to use the same arguments as the first one. If first is set, fail -// if the pipe already exists. -func createPipe(address string, first bool) (syscall.Handle, error) { - n, err := syscall.UTF16PtrFromString(address) - if err != nil { - return 0, err - } - mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED) - if first { - mode |= file_flag_first_pipe_instance - } - return createNamedPipe(n, - mode, - pipe_type_byte, - pipe_unlimited_instances, - 512, 512, 0, nil) -} - -func badAddr(addr string) PipeError { - return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false} -} -func timeout(addr string) PipeError { - return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} -} - - - -func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { - c, err := Dial(cfg.Endpoint) - if err != nil { - return nil, err - } - - return &ipcClient{codec.New(c)}, nil -} - -func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error { - os.Remove(cfg.Endpoint) // in case it still exists from a previous run - - l, err := Listen(cfg.Endpoint) - if err != nil { - return err - } - os.Chmod(cfg.Endpoint, 0600) - - go func() { - for { - conn, err := l.Accept() - if err != nil { - glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err) - continue - } - - go func(conn net.Conn) { - codec := codec.New(conn) - - for { - req, err := codec.ReadRequest() - if err == io.EOF { - codec.Close() - return - } else if err != nil { - glog.V(logger.Error).Infof("IPC recv err - %v\n", err) - codec.Close() - return - } - - var rpcResponse interface{} - res, err := api.Execute(req) - - rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) - err = codec.WriteResponse(rpcResponse) - if err != nil { - glog.V(logger.Error).Infof("IPC send err - %v\n", err) - codec.Close() - return - } - } - }(conn) - } - }() - - glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) - - return nil -} ->>>>>>> fixed windows build problem -- cgit v1.2.3 From 3f94d09c1f07538c3fc72c72609037c47c04c4b5 Mon Sep 17 00:00:00 2001 From: zsfelfoldi Date: Tue, 26 May 2015 14:17:43 +0200 Subject: fixed saving receipts --- rpc/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rpc') diff --git a/rpc/api.go b/rpc/api.go index e35395734..8b9a080f8 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -59,7 +59,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err case "eth_mining": *reply = api.xeth().IsMining() case "eth_gasPrice": - v := xeth.DefaultGasPrice() + v := api.xeth().DefaultGasPrice() *reply = newHexNum(v.Bytes()) case "eth_accounts": *reply = api.xeth().Accounts() -- cgit v1.2.3 From b42b70eb5fd55741175dd5503686843a9d7d043a Mon Sep 17 00:00:00 2001 From: zsfelfoldi Date: Fri, 12 Jun 2015 14:11:10 +0200 Subject: fixed rpc/api.GasPrice --- rpc/api/eth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rpc') diff --git a/rpc/api/eth.go b/rpc/api/eth.go index a0b9dad86..943f19b90 100644 --- a/rpc/api/eth.go +++ b/rpc/api/eth.go @@ -139,7 +139,7 @@ func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) { } func (self *ethApi) GasPrice(req *shared.Request) (interface{}, error) { - return newHexNum(xeth.DefaultGasPrice().Bytes()), nil + return newHexNum(self.xeth.DefaultGasPrice().Bytes()), nil } func (self *ethApi) GetStorage(req *shared.Request) (interface{}, error) { -- cgit v1.2.3 From 2628103f1df35ad6a130f2f41e73c7703bf61886 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 15 Jun 2015 17:21:08 +0200 Subject: rpc/api: fixed default gas-(price) issue. --- rpc/api/eth.go | 9 ++++++++- rpc/api/eth_args.go | 8 ++------ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'rpc') diff --git a/rpc/api/eth.go b/rpc/api/eth.go index a0b9dad86..d329dbf10 100644 --- a/rpc/api/eth.go +++ b/rpc/api/eth.go @@ -259,7 +259,14 @@ func (self *ethApi) SendTransaction(req *shared.Request) (interface{}, error) { nonce = args.Nonce.String() } - v, err := self.xeth.Transact(args.From, args.To, nonce, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) + var gas, price string + if args.Gas != nil { + gas = args.Gas.String() + } + if args.GasPrice != nil { + price = args.GasPrice.String() + } + v, err := self.xeth.Transact(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data) if err != nil { return nil, err } diff --git a/rpc/api/eth_args.go b/rpc/api/eth_args.go index ad9a35fa2..1c86bee51 100644 --- a/rpc/api/eth_args.go +++ b/rpc/api/eth_args.go @@ -333,9 +333,7 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { args.Value = num num = nil - if ext.Gas == nil { - num = big.NewInt(0) - } else { + if ext.Gas != nil { if num, err = numString(ext.Gas); err != nil { return err } @@ -343,9 +341,7 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { args.Gas = num num = nil - if ext.GasPrice == nil { - num = big.NewInt(0) - } else { + if ext.GasPrice != nil { if num, err = numString(ext.GasPrice); err != nil { return err } -- cgit v1.2.3