diff options
Diffstat (limited to 'rpc')
-rw-r--r-- | rpc/api.go | 462 | ||||
-rw-r--r-- | rpc/api_test.go | 101 | ||||
-rw-r--r-- | rpc/args.go | 1080 | ||||
-rw-r--r-- | rpc/args_test.go | 2173 | ||||
-rw-r--r-- | rpc/http.go | 127 | ||||
-rw-r--r-- | rpc/jeth.go | 58 | ||||
-rw-r--r-- | rpc/responses.go | 257 | ||||
-rw-r--r-- | rpc/responses_test.go | 219 | ||||
-rw-r--r-- | rpc/types.go | 269 | ||||
-rw-r--r-- | rpc/types_test.go | 50 |
10 files changed, 4796 insertions, 0 deletions
diff --git a/rpc/api.go b/rpc/api.go new file mode 100644 index 000000000..afd242aa3 --- /dev/null +++ b/rpc/api.go @@ -0,0 +1,462 @@ +package rpc + +import ( + "encoding/json" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/xeth" +) + +type EthereumApi struct { + eth *xeth.XEth + xethMu sync.RWMutex +} + +func NewEthereumApi(xeth *xeth.XEth) *EthereumApi { + api := &EthereumApi{ + eth: xeth, + } + + return api +} + +func (api *EthereumApi) xeth() *xeth.XEth { + api.xethMu.RLock() + defer api.xethMu.RUnlock() + + return api.eth +} + +func (api *EthereumApi) xethAtStateNum(num int64) *xeth.XEth { + return api.xeth().AtStateNum(num) +} + +func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { + // Spec at https://github.com/ethereum/wiki/wiki/JSON-RPC + rpclogger.Debugf("%s %s", req.Method, req.Params) + + switch req.Method { + case "web3_sha3": + args := new(Sha3Args) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = common.ToHex(crypto.Sha3(common.FromHex(args.Data))) + case "web3_clientVersion": + *reply = api.xeth().ClientVersion() + case "net_version": + *reply = api.xeth().NetworkVersion() + case "net_listening": + *reply = api.xeth().IsListening() + case "net_peerCount": + v := api.xeth().PeerCount() + *reply = common.ToHex(big.NewInt(int64(v)).Bytes()) + case "eth_protocolVersion": + *reply = api.xeth().EthVersion() + case "eth_coinbase": + // TODO handling of empty coinbase due to lack of accounts + res := api.xeth().Coinbase() + if res == "0x" || res == "0x0" { + *reply = nil + } else { + *reply = res + } + case "eth_mining": + *reply = api.xeth().IsMining() + case "eth_gasPrice": + v := xeth.DefaultGas() + *reply = common.ToHex(v.Bytes()) + case "eth_accounts": + *reply = api.xeth().Accounts() + case "eth_blockNumber": + v := api.xeth().CurrentBlock().Number() + *reply = common.ToHex(v.Bytes()) + case "eth_getBalance": + args := new(GetBalanceArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + *reply = api.xethAtStateNum(args.BlockNumber).BalanceAt(args.Address) + //v := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance() + //*reply = common.ToHex(v.Bytes()) + case "eth_getStorage", "eth_storageAt": + args := new(GetStorageArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + *reply = api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage() + case "eth_getStorageAt": + args := new(GetStorageAtArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + *reply = api.xethAtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key) + case "eth_getTransactionCount": + args := new(GetTxCountArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + count := api.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address) + *reply = common.ToHex(big.NewInt(int64(count)).Bytes()) + case "eth_getBlockTransactionCountByHash": + args := new(HashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false) + *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes()) + case "eth_getBlockTransactionCountByNumber": + args := new(BlockNumArg) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := NewBlockRes(api.xeth().EthBlockByNumber(args.BlockNumber), false) + *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes()) + case "eth_getUncleCountByBlockHash": + args := new(HashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByHash(args.Hash) + br := NewBlockRes(block, false) + *reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes()) + case "eth_getUncleCountByBlockNumber": + args := new(BlockNumArg) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByNumber(args.BlockNumber) + br := NewBlockRes(block, false) + *reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes()) + case "eth_getData", "eth_getCode": + args := new(GetDataArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address) + *reply = newHexData(v) + case "eth_sendTransaction", "eth_transact": + args := new(NewTxArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := api.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) + if err != nil { + return err + } + *reply = v + case "eth_call": + args := new(CallArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := api.xethAtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) + if err != nil { + return err + } + + *reply = v + case "eth_flush": + return NewNotImplementedError(req.Method) + case "eth_getBlockByHash": + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByHash(args.BlockHash) + br := NewBlockRes(block, args.IncludeTxs) + + *reply = br + case "eth_getBlockByNumber": + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByNumber(args.BlockNumber) + br := NewBlockRes(block, args.IncludeTxs) + + *reply = br + case "eth_getTransactionByHash": + // HashIndexArgs used, but only the "Hash" part we need. + args := new(HashIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + } + tx, bhash, bnum, txi := api.xeth().EthTransactionByHash(args.Hash) + if tx != nil { + v := NewTransactionRes(tx) + v.BlockHash = newHexData(bhash) + v.BlockNumber = newHexNum(bnum) + v.TxIndex = newHexNum(txi) + *reply = v + } + case "eth_getTransactionByBlockHashAndIndex": + args := new(HashIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByHash(args.Hash) + br := NewBlockRes(block, true) + + if args.Index >= int64(len(br.Transactions)) || args.Index < 0 { + return NewValidationError("Index", "does not exist") + } + *reply = br.Transactions[args.Index] + case "eth_getTransactionByBlockNumberAndIndex": + args := new(BlockNumIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByNumber(args.BlockNumber) + v := NewBlockRes(block, true) + + if args.Index >= int64(len(v.Transactions)) || args.Index < 0 { + return NewValidationError("Index", "does not exist") + } + *reply = v.Transactions[args.Index] + case "eth_getUncleByBlockHashAndIndex": + args := new(HashIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + br := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false) + if br == nil { + *reply = nil + return nil + } + + if args.Index >= int64(len(br.Uncles)) || args.Index < 0 { + return NewValidationError("Index", "does not exist") + } + + uhash := br.Uncles[args.Index] + uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), false) + + *reply = uncle + case "eth_getUncleByBlockNumberAndIndex": + args := new(BlockNumIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByNumber(args.BlockNumber) + v := NewBlockRes(block, true) + + if v == nil { + *reply = nil + return nil + } + + if args.Index >= int64(len(v.Uncles)) || args.Index < 0 { + return NewValidationError("Index", "does not exist") + } + + uhash := v.Uncles[args.Index] + uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), false) + + *reply = uncle + case "eth_getCompilers": + c := []string{""} + *reply = c + case "eth_compileSolidity", "eth_compileLLL", "eth_compileSerpent": + return NewNotImplementedError(req.Method) + case "eth_newFilter": + args := new(BlockFilterArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + id := api.xeth().RegisterFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics) + *reply = common.ToHex(big.NewInt(int64(id)).Bytes()) + case "eth_newBlockFilter": + args := new(FilterStringArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + id := api.xeth().NewFilterString(args.Word) + *reply = common.ToHex(big.NewInt(int64(id)).Bytes()) + case "eth_uninstallFilter": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = api.xeth().UninstallFilter(args.Id) + case "eth_getFilterChanges": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = NewLogsRes(api.xeth().FilterChanged(args.Id)) + case "eth_getFilterLogs": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = NewLogsRes(api.xeth().Logs(args.Id)) + case "eth_getLogs": + args := new(BlockFilterArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = NewLogsRes(api.xeth().AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)) + case "eth_getWork": + api.xeth().SetMining(true) + *reply = api.xeth().RemoteMining().GetWork() + case "eth_submitWork": + args := new(SubmitWorkArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = api.xeth().RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)) + case "db_putString": + args := new(DbArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + if err := args.requirements(); err != nil { + return err + } + + api.xeth().DbPut([]byte(args.Database+args.Key), args.Value) + + *reply = true + case "db_getString": + args := new(DbArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + if err := args.requirements(); err != nil { + return err + } + + res, _ := api.xeth().DbGet([]byte(args.Database + args.Key)) + *reply = string(res) + case "db_putHex": + args := new(DbHexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + if err := args.requirements(); err != nil { + return err + } + + api.xeth().DbPut([]byte(args.Database+args.Key), args.Value) + *reply = true + case "db_getHex": + args := new(DbHexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + if err := args.requirements(); err != nil { + return err + } + + res, _ := api.xeth().DbGet([]byte(args.Database + args.Key)) + *reply = common.ToHex(res) + case "shh_version": + *reply = api.xeth().WhisperVersion() + case "shh_post": + args := new(WhisperMessageArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + err := api.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl) + if err != nil { + return err + } + + *reply = true + case "shh_newIdentity": + *reply = api.xeth().Whisper().NewIdentity() + // case "shh_removeIdentity": + // args := new(WhisperIdentityArgs) + // if err := json.Unmarshal(req.Params, &args); err != nil { + // return err + // } + // *reply = api.xeth().Whisper().RemoveIdentity(args.Identity) + case "shh_hasIdentity": + args := new(WhisperIdentityArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = api.xeth().Whisper().HasIdentity(args.Identity) + case "shh_newGroup", "shh_addToGroup": + return NewNotImplementedError(req.Method) + case "shh_newFilter": + args := new(WhisperFilterArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + opts := new(xeth.Options) + // opts.From = args.From + opts.To = args.To + opts.Topics = args.Topics + id := api.xeth().NewWhisperFilter(opts) + *reply = common.ToHex(big.NewInt(int64(id)).Bytes()) + case "shh_uninstallFilter": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = api.xeth().UninstallWhisperFilter(args.Id) + case "shh_getFilterChanges": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = api.xeth().MessagesChanged(args.Id) + case "shh_getMessages": + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = api.xeth().Whisper().Messages(args.Id) + + // case "eth_register": + // // Placeholder for actual type + // args := new(HashIndexArgs) + // if err := json.Unmarshal(req.Params, &args); err != nil { + // return err + // } + // *reply = api.xeth().Register(args.Hash) + // case "eth_unregister": + // args := new(HashIndexArgs) + // if err := json.Unmarshal(req.Params, &args); err != nil { + // return err + // } + // *reply = api.xeth().Unregister(args.Hash) + // case "eth_watchTx": + // args := new(HashIndexArgs) + // if err := json.Unmarshal(req.Params, &args); err != nil { + // return err + // } + // *reply = api.xeth().PullWatchTx(args.Hash) + default: + return NewNotImplementedError(req.Method) + } + + rpclogger.DebugDetailf("Reply: %T %s", reply, reply) + return nil +} diff --git a/rpc/api_test.go b/rpc/api_test.go new file mode 100644 index 000000000..ac9b67fac --- /dev/null +++ b/rpc/api_test.go @@ -0,0 +1,101 @@ +package rpc + +import ( + "encoding/json" + // "sync" + "testing" + // "time" + + // "github.com/ethereum/go-ethereum/xeth" +) + +func TestWeb3Sha3(t *testing.T) { + jsonstr := `{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}` + expected := "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" + + api := &EthereumApi{} + + var req RpcRequest + json.Unmarshal([]byte(jsonstr), &req) + + var response interface{} + _ = api.GetRequestReply(&req, &response) + + if response.(string) != expected { + t.Errorf("Expected %s got %s", expected, response) + } +} + +// func TestDbStr(t *testing.T) { +// jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}` +// jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}` +// expected := "myString" + +// xeth := &xeth.XEth{} +// api := NewEthereumApi(xeth) +// var response interface{} + +// var req RpcRequest +// json.Unmarshal([]byte(jsonput), &req) +// _ = api.GetRequestReply(&req, &response) + +// json.Unmarshal([]byte(jsonget), &req) +// _ = api.GetRequestReply(&req, &response) + +// if response.(string) != expected { +// t.Errorf("Expected %s got %s", expected, response) +// } +// } + +// func TestDbHexStr(t *testing.T) { +// jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}` +// jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}` +// expected := "0xbeef" + +// xeth := &xeth.XEth{} +// api := NewEthereumApi(xeth) +// defer api.db.Close() +// var response interface{} + +// var req RpcRequest +// json.Unmarshal([]byte(jsonput), &req) +// _ = api.GetRequestReply(&req, &response) + +// json.Unmarshal([]byte(jsonget), &req) +// _ = api.GetRequestReply(&req, &response) + +// if response.(string) != expected { +// t.Errorf("Expected %s got %s", expected, response) +// } +// } + +// func TestFilterClose(t *testing.T) { +// t.Skip() +// api := &EthereumApi{ +// logs: make(map[int]*logFilter), +// messages: make(map[int]*whisperFilter), +// quit: make(chan struct{}), +// } + +// filterTickerTime = 1 +// api.logs[0] = &logFilter{} +// api.messages[0] = &whisperFilter{} +// var wg sync.WaitGroup +// wg.Add(1) +// go api.start() +// go func() { +// select { +// case <-time.After(500 * time.Millisecond): +// api.stop() +// wg.Done() +// } +// }() +// wg.Wait() +// if len(api.logs) != 0 { +// t.Error("expected logs to be empty") +// } + +// if len(api.messages) != 0 { +// t.Error("expected messages to be empty") +// } +// } diff --git a/rpc/args.go b/rpc/args.go new file mode 100644 index 000000000..70618a01a --- /dev/null +++ b/rpc/args.go @@ -0,0 +1,1080 @@ +package rpc + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + defaultLogLimit = 100 + defaultLogOffset = 0 +) + +func blockHeightFromJson(msg json.RawMessage, number *int64) error { + var raw interface{} + if err := json.Unmarshal(msg, &raw); err != nil { + return NewDecodeParamError(err.Error()) + } + return blockHeight(raw, number) +} + +func blockHeight(raw interface{}, number *int64) error { + // Parse as integer + num, ok := raw.(float64) + if ok { + *number = int64(num) + return nil + } + + // Parse as string/hexstring + str, ok := raw.(string) + if !ok { + return NewInvalidTypeError("", "not a number or string") + } + + switch str { + case "latest": + *number = -1 + case "pending": + *number = -2 + default: + *number = common.String2Big(str).Int64() + } + + return nil +} + +func numString(raw interface{}, number *int64) error { + // Parse as integer + num, ok := raw.(float64) + if ok { + *number = int64(num) + return nil + } + + // Parse as string/hexstring + str, ok := raw.(string) + if !ok { + return NewInvalidTypeError("", "not a number or string") + } + *number = common.String2Big(str).Int64() + + return nil +} + +// func toNumber(v interface{}) (int64, error) { +// var str string +// if v != nil { +// var ok bool +// str, ok = v.(string) +// if !ok { +// return 0, errors.New("is not a string or undefined") +// } +// } else { +// str = "latest" +// } + +// switch str { +// case "latest": +// return -1, nil +// default: +// return int64(common.Big(v.(string)).Int64()), nil +// } +// } + +// func hashString(raw interface{}, hash *string) error { +// argstr, ok := raw.(string) +// if !ok { +// return NewInvalidTypeError("", "not a string") +// } +// v := common.IsHex(argstr) +// hash = &argstr + +// return nil +// } + +type GetBlockByHashArgs struct { + BlockHash string + IncludeTxs bool +} + +func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return NewInsufficientParamsError(len(obj), 2) + } + + argstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("blockHash", "not a string") + } + args.BlockHash = argstr + + args.IncludeTxs = obj[1].(bool) + + return nil +} + +type GetBlockByNumberArgs struct { + BlockNumber int64 + IncludeTxs bool +} + +func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return NewInsufficientParamsError(len(obj), 2) + } + + if v, ok := obj[0].(float64); ok { + args.BlockNumber = int64(v) + } else if v, ok := obj[0].(string); ok { + args.BlockNumber = common.Big(v).Int64() + } else { + return NewInvalidTypeError("blockNumber", "not a number or string") + } + + args.IncludeTxs = obj[1].(bool) + + return nil +} + +type NewTxArgs struct { + From string + To string + 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 + Value interface{} + Gas interface{} + GasPrice interface{} + Data string + } + + // Decode byte slice to array of RawMessages + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + // Check for sufficient params + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + // Decode 0th RawMessage to temporary struct + if err := json.Unmarshal(obj[0], &ext); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(ext.From) == 0 { + return NewValidationError("from", "is required") + } + + args.From = ext.From + args.To = ext.To + args.Data = ext.Data + + var num int64 + if ext.Value == nil { + num = 0 + } else { + if err := numString(ext.Value, &num); err != nil { + return err + } + } + args.Value = big.NewInt(num) + + if ext.Gas == nil { + num = 0 + } else { + if err := numString(ext.Gas, &num); err != nil { + return err + } + } + args.Gas = big.NewInt(num) + + if ext.GasPrice == nil { + num = 0 + } else { + if err := numString(ext.GasPrice, &num); err != nil { + return err + } + } + args.GasPrice = big.NewInt(num) + + // Check for optional BlockNumber param + if len(obj) > 1 { + if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type CallArgs struct { + From string + To string + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data string + + BlockNumber int64 +} + +func (args *CallArgs) UnmarshalJSON(b []byte) (err error) { + var obj []json.RawMessage + var ext struct { + From string + To string + Value interface{} + Gas interface{} + GasPrice interface{} + Data string + } + + // Decode byte slice to array of RawMessages + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + // Check for sufficient params + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + // Decode 0th RawMessage to temporary struct + if err := json.Unmarshal(obj[0], &ext); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(ext.From) == 0 { + return NewValidationError("from", "is required") + } + args.From = ext.From + + if len(ext.To) == 0 { + return NewValidationError("to", "is required") + } + args.To = ext.To + + var num int64 + if ext.Value == nil { + num = int64(0) + } else { + if err := numString(ext.Value, &num); err != nil { + return err + } + } + args.Value = big.NewInt(num) + + if ext.Gas == nil { + num = int64(0) + } else { + if err := numString(ext.Gas, &num); err != nil { + return err + } + } + args.Gas = big.NewInt(num) + + if ext.GasPrice == nil { + num = int64(0) + } else { + if err := numString(ext.GasPrice, &num); err != nil { + return err + } + } + args.GasPrice = big.NewInt(num) + + args.Data = ext.Data + + // Check for optional BlockNumber param + if len(obj) > 1 { + if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type GetStorageArgs struct { + Address string + BlockNumber int64 +} + +func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type GetStorageAtArgs struct { + Address string + Key string + BlockNumber int64 +} + +func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return NewInsufficientParamsError(len(obj), 2) + } + + addstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + keystr, ok := obj[1].(string) + if !ok { + return NewInvalidTypeError("key", "not a string") + } + args.Key = keystr + + if len(obj) > 2 { + if err := blockHeight(obj[2], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type GetTxCountArgs struct { + Address string + BlockNumber int64 +} + +func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type GetBalanceArgs struct { + Address string + BlockNumber int64 +} + +func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type GetDataArgs struct { + Address string + BlockNumber int64 +} + +func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + addstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("address", "not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } else { + args.BlockNumber = -1 + } + + return nil +} + +type BlockNumArg struct { + BlockNumber int64 +} + +func (args *BlockNumArg) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + if err := blockHeight(obj[0], &args.BlockNumber); err != nil { + return err + } + + return nil +} + +type BlockNumIndexArgs struct { + BlockNumber int64 + Index int64 +} + +func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return NewInsufficientParamsError(len(obj), 2) + } + + if err := blockHeight(obj[0], &args.BlockNumber); err != nil { + return err + } + + arg1, ok := obj[1].(string) + if !ok { + return NewInvalidTypeError("index", "not a string") + } + args.Index = common.Big(arg1).Int64() + + return nil +} + +type HashArgs struct { + Hash string +} + +func (args *HashArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + arg0, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("hash", "not a string") + } + args.Hash = arg0 + + return nil +} + +type HashIndexArgs struct { + Hash string + Index int64 +} + +func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return NewInsufficientParamsError(len(obj), 2) + } + + arg0, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("hash", "not a string") + } + args.Hash = arg0 + + arg1, ok := obj[1].(string) + if !ok { + return NewInvalidTypeError("index", "not a string") + } + args.Index = common.Big(arg1).Int64() + + return nil +} + +type Sha3Args struct { + Data string +} + +func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + argstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("data", "is not a string") + } + args.Data = argstr + return nil +} + +type BlockFilterArgs struct { + Earliest int64 + Latest int64 + Address []string + Topics [][]string + Skip int + Max int +} + +func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + FromBlock interface{} `json:"fromBlock"` + ToBlock interface{} `json:"toBlock"` + Limit interface{} `json:"limit"` + Offset interface{} `json:"offset"` + Address interface{} `json:"address"` + Topics interface{} `json:"topics"` + } + + if err = json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + // args.Earliest, err = toNumber(obj[0].ToBlock) + // if err != nil { + // return NewDecodeParamError(fmt.Sprintf("FromBlock %v", err)) + // } + // args.Latest, err = toNumber(obj[0].FromBlock) + // if err != nil { + // return NewDecodeParamError(fmt.Sprintf("ToBlock %v", err)) + + var num int64 + + // 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 { + num = defaultLogLimit + } else { + if err := numString(obj[0].Limit, &num); err != nil { + return err + } + } + args.Max = int(num) + + if obj[0].Offset == nil { + num = defaultLogOffset + } else { + if err := numString(obj[0].Offset, &num); err != nil { + return err + } + } + args.Skip = int(num) + + if obj[0].Address != nil { + marg, ok := obj[0].Address.([]interface{}) + if ok { + v := make([]string, len(marg)) + for i, arg := range marg { + argstr, ok := arg.(string) + if !ok { + return NewInvalidTypeError(fmt.Sprintf("address[%d]", i), "is not a string") + } + v[i] = argstr + } + args.Address = v + } else { + argstr, ok := obj[0].Address.(string) + if ok { + v := make([]string, 1) + v[0] = argstr + args.Address = v + } else { + return NewInvalidTypeError("address", "is not a string or array") + } + } + } + + if obj[0].Topics != nil { + other, ok := obj[0].Topics.([]interface{}) + if ok { + topicdbl := make([][]string, len(other)) + for i, iv := range other { + if argstr, ok := iv.(string); ok { + // Found a string, push into first element of array + topicsgl := make([]string, 1) + topicsgl[0] = argstr + topicdbl[i] = topicsgl + } else if argarray, ok := iv.([]interface{}); ok { + // Found an array of other + topicdbl[i] = make([]string, len(argarray)) + for j, jv := range argarray { + if v, ok := jv.(string); ok { + topicdbl[i][j] = v + } else { + return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string") + } + } + } else { + return NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array") + } + } + args.Topics = topicdbl + return nil + } else { + return NewInvalidTypeError("topic", "is not a string or array") + } + } + + return nil +} + +type DbArgs struct { + Database string + Key string + Value []byte +} + +func (args *DbArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return NewInsufficientParamsError(len(obj), 2) + } + + var objstr string + var ok bool + + if objstr, ok = obj[0].(string); !ok { + return NewInvalidTypeError("database", "not a string") + } + args.Database = objstr + + if objstr, ok = obj[1].(string); !ok { + return NewInvalidTypeError("key", "not a string") + } + args.Key = objstr + + if len(obj) > 2 { + objstr, ok = obj[2].(string) + if !ok { + return NewInvalidTypeError("value", "not a string") + } + + args.Value = []byte(objstr) + } + + return nil +} + +func (a *DbArgs) requirements() error { + if len(a.Database) == 0 { + return NewValidationError("Database", "cannot be blank") + } + if len(a.Key) == 0 { + return NewValidationError("Key", "cannot be blank") + } + return nil +} + +type DbHexArgs struct { + Database string + Key string + Value []byte +} + +func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 2 { + return NewInsufficientParamsError(len(obj), 2) + } + + var objstr string + var ok bool + + if objstr, ok = obj[0].(string); !ok { + return NewInvalidTypeError("database", "not a string") + } + args.Database = objstr + + if objstr, ok = obj[1].(string); !ok { + return NewInvalidTypeError("key", "not a string") + } + args.Key = objstr + + if len(obj) > 2 { + objstr, ok = obj[2].(string) + if !ok { + return NewInvalidTypeError("value", "not a string") + } + + args.Value = common.FromHex(objstr) + } + + return nil +} + +func (a *DbHexArgs) requirements() error { + if len(a.Database) == 0 { + return NewValidationError("Database", "cannot be blank") + } + if len(a.Key) == 0 { + return NewValidationError("Key", "cannot be blank") + } + return nil +} + +type WhisperMessageArgs struct { + Payload string + To string + From string + Topics []string + Priority uint32 + Ttl uint32 +} + +func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + Payload string + To string + From string + Topics []string + Priority interface{} + Ttl interface{} + } + + if err = json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + args.Payload = obj[0].Payload + args.To = obj[0].To + args.From = obj[0].From + args.Topics = obj[0].Topics + + var num int64 + if err := numString(obj[0].Priority, &num); err != nil { + return err + } + args.Priority = uint32(num) + + if err := numString(obj[0].Ttl, &num); err != nil { + return err + } + args.Ttl = uint32(num) + + return nil +} + +type CompileArgs struct { + Source string +} + +func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + argstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("arg0", "is not a string") + } + args.Source = argstr + + return nil +} + +type FilterStringArgs struct { + Word string +} + +func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + var argstr string + argstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("filter", "not a string") + } + switch argstr { + case "latest", "pending": + break + default: + return NewValidationError("Word", "Must be `latest` or `pending`") + } + args.Word = argstr + return nil +} + +type FilterIdArgs struct { + Id int +} + +func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + var num int64 + if err := numString(obj[0], &num); err != nil { + return err + } + args.Id = int(num) + + return nil +} + +type WhisperIdentityArgs struct { + Identity string +} + +func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + argstr, ok := obj[0].(string) + if !ok { + return NewInvalidTypeError("arg0", "not a string") + } + // if !common.IsHex(argstr) { + // return NewValidationError("arg0", "not a hexstring") + // } + args.Identity = argstr + + return nil +} + +type WhisperFilterArgs struct { + To string `json:"to"` + From string + Topics []string +} + +func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + To interface{} + Topics []interface{} + } + + if err = json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + var argstr string + argstr, ok := obj[0].To.(string) + if !ok { + return NewInvalidTypeError("to", "is not a string") + } + args.To = argstr + + t := make([]string, len(obj[0].Topics)) + for i, j := range obj[0].Topics { + argstr, ok := j.(string) + if !ok { + return NewInvalidTypeError("topics["+string(i)+"]", "is not a string") + } + t[i] = argstr + } + args.Topics = t + + return nil +} + +type SubmitWorkArgs struct { + Nonce uint64 + Header string + Digest string +} + +func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err = json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 3 { + return NewInsufficientParamsError(len(obj), 3) + } + + var objstr string + var ok bool + if objstr, ok = obj[0].(string); !ok { + return NewInvalidTypeError("nonce", "not a string") + } + + args.Nonce = common.String2Big(objstr).Uint64() + if objstr, ok = obj[1].(string); !ok { + return NewInvalidTypeError("header", "not a string") + } + + args.Header = objstr + + if objstr, ok = obj[2].(string); !ok { + return NewInvalidTypeError("digest", "not a string") + } + + args.Digest = objstr + + return nil +} diff --git a/rpc/args_test.go b/rpc/args_test.go new file mode 100644 index 000000000..902f8013e --- /dev/null +++ b/rpc/args_test.go @@ -0,0 +1,2173 @@ +package rpc + +import ( + "bytes" + "encoding/json" + "fmt" + "math/big" + "testing" +) + +func ExpectValidationError(err error) string { + var str string + switch err.(type) { + case nil: + str = "Expected error but didn't get one" + case *ValidationError: + break + default: + str = fmt.Sprintf("Expected *rpc.ValidationError but got %T with message `%s`", err, err.Error()) + } + return str +} + +func ExpectInvalidTypeError(err error) string { + var str string + switch err.(type) { + case nil: + str = "Expected error but didn't get one" + case *InvalidTypeError: + break + default: + str = fmt.Sprintf("Expected *rpc.InvalidTypeError but got %T with message `%s`", err, err.Error()) + } + return str +} + +func ExpectInsufficientParamsError(err error) string { + var str string + switch err.(type) { + case nil: + str = "Expected error but didn't get one" + case *InsufficientParamsError: + break + default: + str = fmt.Sprintf("Expected *rpc.InsufficientParamsError but got %T with message %s", err, err.Error()) + } + return str +} + +func ExpectDecodeParamError(err error) string { + var str string + switch err.(type) { + case nil: + str = "Expected error but didn't get one" + case *DecodeParamError: + break + default: + str = fmt.Sprintf("Expected *rpc.DecodeParamError but got %T with message `%s`", err, err.Error()) + } + return str +} + +func TestSha3(t *testing.T) { + input := `["0x68656c6c6f20776f726c64"]` + expected := "0x68656c6c6f20776f726c64" + + args := new(Sha3Args) + json.Unmarshal([]byte(input), &args) + + if args.Data != expected { + t.Error("got %s expected %s", input, expected) + } +} + +func TestSha3ArgsInvalid(t *testing.T) { + input := `{}` + + args := new(Sha3Args) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestSha3ArgsEmpty(t *testing.T) { + input := `[]` + + args := new(Sha3Args) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} +func TestSha3ArgsDataInvalid(t *testing.T) { + input := `[4]` + + args := new(Sha3Args) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBalanceArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x1f"]` + expected := new(GetBalanceArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = 31 + + args := new(GetBalanceArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.Address != expected.Address { + t.Errorf("Address should be %v but is %v", expected.Address, args.Address) + } + + if args.BlockNumber != expected.BlockNumber { + t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetBalanceArgsLatest(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` + expected := new(GetBalanceArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = -1 + + args := new(GetBalanceArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.Address != expected.Address { + t.Errorf("Address should be %v but is %v", expected.Address, args.Address) + } + + if args.BlockNumber != expected.BlockNumber { + t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetBalanceArgsEmpty(t *testing.T) { + input := `[]` + + args := new(GetBalanceArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBalanceArgsInvalid(t *testing.T) { + input := `6` + + args := new(GetBalanceArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBalanceArgsBlockInvalid(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", false]` + + args := new(GetBalanceArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBalanceArgsAddressInvalid(t *testing.T) { + input := `[-9, "latest"]` + + args := new(GetBalanceArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBlockByHashArgs(t *testing.T) { + input := `["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true]` + expected := new(GetBlockByHashArgs) + expected.BlockHash = "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" + expected.IncludeTxs = true + + args := new(GetBlockByHashArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.BlockHash != expected.BlockHash { + t.Errorf("BlockHash should be %v but is %v", expected.BlockHash, args.BlockHash) + } + + if args.IncludeTxs != expected.IncludeTxs { + t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs) + } +} + +func TestGetBlockByHashArgsEmpty(t *testing.T) { + input := `[]` + + args := new(GetBlockByHashArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBlockByHashArgsInvalid(t *testing.T) { + input := `{}` + + args := new(GetBlockByHashArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBlockByHashArgsHashInt(t *testing.T) { + input := `[8]` + + args := new(GetBlockByHashArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBlockByNumberArgsBlockNum(t *testing.T) { + input := `[436, false]` + expected := new(GetBlockByNumberArgs) + expected.BlockNumber = 436 + expected.IncludeTxs = false + + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.BlockNumber != expected.BlockNumber { + t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) + } + + if args.IncludeTxs != expected.IncludeTxs { + t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs) + } +} + +func TestGetBlockByNumberArgsBlockHex(t *testing.T) { + input := `["0x1b4", false]` + expected := new(GetBlockByNumberArgs) + expected.BlockNumber = 436 + expected.IncludeTxs = false + + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.BlockNumber != expected.BlockNumber { + t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) + } + + if args.IncludeTxs != expected.IncludeTxs { + t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs) + } +} + +func TestGetBlockByNumberEmpty(t *testing.T) { + input := `[]` + + args := new(GetBlockByNumberArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBlockByNumberShort(t *testing.T) { + input := `["0xbbb"]` + + args := new(GetBlockByNumberArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetBlockByNumberBool(t *testing.T) { + input := `[true, true]` + + args := new(GetBlockByNumberArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} +func TestGetBlockByNumberBlockObject(t *testing.T) { + input := `{}` + + args := new(GetBlockByNumberArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestNewTxArgs(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, + "0x10"]` + expected := new(NewTxArgs) + expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155" + expected.To = "0xd46e8dd67c5d32be8058bb8eb970870f072445675" + expected.Gas = big.NewInt(30400) + expected.GasPrice = big.NewInt(10000000000000) + expected.Value = big.NewInt(10000000000000) + expected.Data = "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + expected.BlockNumber = big.NewInt(16).Int64() + + args := new(NewTxArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { + t.Errorf("Gas shoud be %#v but is %#v", expected.Gas.Bytes(), args.Gas.Bytes()) + } + + if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %#v but is %#v", expected.GasPrice, args.GasPrice) + } + + if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { + t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) + } + + if expected.Data != args.Data { + t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestNewTxArgsInt(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": 100, + "gasPrice": 50, + "value": 8765456789, + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, + 5]` + expected := new(NewTxArgs) + expected.Gas = big.NewInt(100) + expected.GasPrice = big.NewInt(50) + expected.Value = big.NewInt(8765456789) + expected.BlockNumber = int64(5) + + args := new(NewTxArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { + t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas) + } + + if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice) + } + + if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { + t.Errorf("Value shoud be %v but is %v", expected.Value, args.Value) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %v but is %v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestNewTxArgsBlockBool(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, + false]` + + args := new(NewTxArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestNewTxArgsGasInvalid(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": false, + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(NewTxArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestNewTxArgsGaspriceInvalid(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": false, + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(NewTxArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestNewTxArgsValueInvalid(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": false, + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(NewTxArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestNewTxArgsGasMissing(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + expected := new(NewTxArgs) + expected.Gas = big.NewInt(0) + + args := new(NewTxArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { + t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas) + } +} + +func TestNewTxArgsBlockGaspriceMissing(t *testing.T) { + input := `[{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + expected := new(NewTxArgs) + expected.GasPrice = big.NewInt(0) + + args := new(NewTxArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice) + } + +} + +func TestNewTxArgsValueMissing(t *testing.T) { + input := `[{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + expected := new(NewTxArgs) + expected.Value = big.NewInt(0) + + args := new(NewTxArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { + t.Errorf("Value shoud be %v but is %v", expected.Value, args.Value) + } + +} + +func TestNewTxArgsEmpty(t *testing.T) { + input := `[]` + + args := new(NewTxArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestNewTxArgsInvalid(t *testing.T) { + input := `{}` + + args := new(NewTxArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} +func TestNewTxArgsNotStrings(t *testing.T) { + input := `[{"from":6}]` + + args := new(NewTxArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestNewTxArgsFromEmpty(t *testing.T) { + input := `[{"to": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]` + + args := new(NewTxArgs) + str := ExpectValidationError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCallArgs(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, + "0x10"]` + expected := new(CallArgs) + expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155" + expected.To = "0xd46e8dd67c5d32be8058bb8eb970870f072445675" + expected.Gas = big.NewInt(30400) + expected.GasPrice = big.NewInt(10000000000000) + expected.Value = big.NewInt(10000000000000) + expected.Data = "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + expected.BlockNumber = big.NewInt(16).Int64() + + args := new(CallArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { + t.Errorf("Gas shoud be %#v but is %#v", expected.Gas.Bytes(), args.Gas.Bytes()) + } + + if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %#v but is %#v", expected.GasPrice, args.GasPrice) + } + + if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { + t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) + } + + if expected.Data != args.Data { + t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestCallArgsInt(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": 100, + "gasPrice": 50, + "value": 8765456789, + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, + 5]` + expected := new(CallArgs) + expected.Gas = big.NewInt(100) + expected.GasPrice = big.NewInt(50) + expected.Value = big.NewInt(8765456789) + expected.BlockNumber = int64(5) + + args := new(CallArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { + t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas) + } + + if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice) + } + + if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { + t.Errorf("Value shoud be %v but is %v", expected.Value, args.Value) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %v but is %v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestCallArgsBlockBool(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, + false]` + + args := new(CallArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCallArgsGasInvalid(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": false, + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(CallArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCallArgsGaspriceInvalid(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": false, + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(CallArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCallArgsValueInvalid(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": false, + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(CallArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCallArgsGasMissing(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(CallArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + expected := new(CallArgs) + expected.Gas = big.NewInt(0) + + if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { + t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas) + } + +} + +func TestCallArgsBlockGaspriceMissing(t *testing.T) { + input := `[{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(CallArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + expected := new(CallArgs) + expected.GasPrice = big.NewInt(0) + + if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice) + } +} + +func TestCallArgsValueMissing(t *testing.T) { + input := `[{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }]` + + args := new(CallArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + expected := new(CallArgs) + expected.Value = big.NewInt(int64(0)) + + if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %v but is %v", expected.Value, args.Value) + } +} + +func TestCallArgsEmpty(t *testing.T) { + input := `[]` + + args := new(CallArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCallArgsInvalid(t *testing.T) { + input := `{}` + + args := new(CallArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} +func TestCallArgsNotStrings(t *testing.T) { + input := `[{"from":6}]` + + args := new(CallArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCallArgsFromEmpty(t *testing.T) { + input := `[{"to": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]` + + args := new(CallArgs) + str := ExpectValidationError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCallArgsToEmpty(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]` + + args := new(CallArgs) + str := ExpectValidationError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` + expected := new(GetStorageArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = -1 + + args := new(GetStorageArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetStorageInvalidArgs(t *testing.T) { + input := `{}` + + args := new(GetStorageArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageInvalidBlockheight(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", {}]` + + args := new(GetStorageArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageEmptyArgs(t *testing.T) { + input := `[]` + + args := new(GetStorageArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageAddressInt(t *testing.T) { + input := `[32456785432456, "latest"]` + + args := new(GetStorageArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageAtArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x0", "0x2"]` + expected := new(GetStorageAtArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.Key = "0x0" + expected.BlockNumber = 2 + + args := new(GetStorageAtArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.Key != args.Key { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetStorageAtEmptyArgs(t *testing.T) { + input := `[]` + + args := new(GetStorageAtArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageAtArgsInvalid(t *testing.T) { + input := `{}` + + args := new(GetStorageAtArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageAtArgsAddressNotString(t *testing.T) { + input := `[true, "0x0", "0x2"]` + + args := new(GetStorageAtArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageAtArgsKeyNotString(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", true, "0x2"]` + + args := new(GetStorageAtArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetStorageAtArgsValueNotString(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x1", true]` + + args := new(GetStorageAtArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetTxCountArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "pending"]` + expected := new(GetTxCountArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = -2 + + args := new(GetTxCountArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetTxCountEmptyArgs(t *testing.T) { + input := `[]` + + args := new(GetTxCountArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetTxCountEmptyArgsInvalid(t *testing.T) { + input := `false` + + args := new(GetTxCountArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetTxCountAddressNotString(t *testing.T) { + input := `[false, "pending"]` + + args := new(GetTxCountArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetTxCountBlockheightInvalid(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", {}]` + + args := new(GetTxCountArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetDataArgs(t *testing.T) { + input := `["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", "latest"]` + expected := new(GetDataArgs) + expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" + expected.BlockNumber = -1 + + args := new(GetDataArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetDataArgsEmpty(t *testing.T) { + input := `[]` + + args := new(GetDataArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetDataArgsInvalid(t *testing.T) { + input := `{}` + + args := new(GetDataArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetDataArgsAddressNotString(t *testing.T) { + input := `[12, "latest"]` + + args := new(GetDataArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestGetDataArgsBlocknumberNotString(t *testing.T) { + input := `["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", false]` + + args := new(GetDataArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgs(t *testing.T) { + input := `[{ + "fromBlock": "0x1", + "toBlock": "0x2", + "limit": "0x3", + "offset": "0x0", + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": + [ + ["0xAA", "0xBB"], + ["0xCC", "0xDD"] + ] + }]` + + expected := new(BlockFilterArgs) + expected.Earliest = 1 + expected.Latest = 2 + expected.Max = 3 + expected.Skip = 0 + expected.Address = []string{"0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"} + expected.Topics = [][]string{ + []string{"0xAA", "0xBB"}, + []string{"0xCC", "0xDD"}, + } + + args := new(BlockFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Earliest != args.Earliest { + t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) + } + + if expected.Latest != args.Latest { + t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest) + } + + if expected.Max != args.Max { + t.Errorf("Max shoud be %#v but is %#v", expected.Max, args.Max) + } + + if expected.Skip != args.Skip { + t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip) + } + + if expected.Address[0] != args.Address[0] { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.Topics[0][0] != args.Topics[0][0] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + if expected.Topics[0][1] != args.Topics[0][1] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + if expected.Topics[1][0] != args.Topics[1][0] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + if expected.Topics[1][1] != args.Topics[1][1] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + +} + +func TestBlockFilterArgsDefaults(t *testing.T) { + input := `[{ + "address": ["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"], + "topics": ["0xAA","0xBB"] + }]` + expected := new(BlockFilterArgs) + expected.Earliest = -1 + expected.Latest = -1 + expected.Max = 100 + expected.Skip = 0 + expected.Address = []string{"0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"} + expected.Topics = [][]string{[]string{"0xAA"}, []string{"0xBB"}} + + args := new(BlockFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Earliest != args.Earliest { + t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) + } + + if expected.Latest != args.Latest { + t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest) + } + + if expected.Max != args.Max { + t.Errorf("Max shoud be %#v but is %#v", expected.Max, args.Max) + } + + if expected.Skip != args.Skip { + t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip) + } + + if expected.Address[0] != args.Address[0] { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.Topics[0][0] != args.Topics[0][0] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + + if expected.Topics[1][0] != args.Topics[1][0] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } +} + +func TestBlockFilterArgsWords(t *testing.T) { + input := `[{ + "fromBlock": "latest", + "toBlock": "pending" + }]` + expected := new(BlockFilterArgs) + expected.Earliest = -1 + expected.Latest = -2 + + args := new(BlockFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Earliest != args.Earliest { + t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) + } + + if expected.Latest != args.Latest { + t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest) + } +} + +func TestBlockFilterArgsInvalid(t *testing.T) { + input := `{}` + + args := new(BlockFilterArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsFromBool(t *testing.T) { + input := `[{ + "fromBlock": true, + "toBlock": "pending" + }]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsToBool(t *testing.T) { + input := `[{ + "fromBlock": "pending", + "toBlock": true + }]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsEmptyArgs(t *testing.T) { + input := `[]` + + args := new(BlockFilterArgs) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +func TestBlockFilterArgsLimitInvalid(t *testing.T) { + input := `[{"limit": false}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsOffsetInvalid(t *testing.T) { + input := `[{"offset": true}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsAddressInt(t *testing.T) { + input := `[{ + "address": 1, + "topics": "0x12341234"}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsAddressSliceInt(t *testing.T) { + input := `[{ + "address": [1], + "topics": "0x12341234"}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsTopicInt(t *testing.T) { + input := `[{ + "address": ["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"], + "topics": 1}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsTopicSliceInt(t *testing.T) { + input := `[{ + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": [1]}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsTopicSliceInt2(t *testing.T) { + input := `[{ + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": ["0xAA", [1]]}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsTopicComplex(t *testing.T) { + input := `[{ + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": ["0xAA", ["0xBB", "0xCC"]] + }]` + + args := new(BlockFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + fmt.Printf("%v\n", args) + return + } + + if args.Topics[0][0] != "0xAA" { + t.Errorf("Topic should be %s but is %s", "0xAA", args.Topics[0][0]) + } + + if args.Topics[1][0] != "0xBB" { + t.Errorf("Topic should be %s but is %s", "0xBB", args.Topics[0][0]) + } + + if args.Topics[1][1] != "0xCC" { + t.Errorf("Topic should be %s but is %s", "0xCC", args.Topics[0][0]) + } +} + +func TestDbArgs(t *testing.T) { + input := `["testDB","myKey","0xbeef"]` + expected := new(DbArgs) + expected.Database = "testDB" + expected.Key = "myKey" + expected.Value = []byte("0xbeef") + + args := new(DbArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Database != args.Database { + t.Errorf("Database shoud be %#v but is %#v", expected.Database, args.Database) + } + + if expected.Key != args.Key { + t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key) + } + + if bytes.Compare(expected.Value, args.Value) != 0 { + t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) + } +} + +func TestDbArgsInvalid(t *testing.T) { + input := `{}` + + args := new(DbArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbArgsEmpty(t *testing.T) { + input := `[]` + + args := new(DbArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbArgsDatabaseType(t *testing.T) { + input := `[true, "keyval", "valval"]` + + args := new(DbArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbArgsKeyType(t *testing.T) { + input := `["dbval", 3, "valval"]` + + args := new(DbArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbArgsValType(t *testing.T) { + input := `["dbval", "keyval", {}]` + + args := new(DbArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbArgsDatabaseEmpty(t *testing.T) { + input := `["", "keyval", "valval"]` + + args := new(DbArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err.Error()) + } + + str := ExpectValidationError(args.requirements()) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbArgsKeyEmpty(t *testing.T) { + input := `["dbval", "", "valval"]` + + args := new(DbArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err.Error()) + } + + str := ExpectValidationError(args.requirements()) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbHexArgs(t *testing.T) { + input := `["testDB","myKey","0xbeef"]` + expected := new(DbHexArgs) + expected.Database = "testDB" + expected.Key = "myKey" + expected.Value = []byte{0xbe, 0xef} + + args := new(DbHexArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Database != args.Database { + t.Errorf("Database shoud be %#v but is %#v", expected.Database, args.Database) + } + + if expected.Key != args.Key { + t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key) + } + + if bytes.Compare(expected.Value, args.Value) != 0 { + t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) + } +} + +func TestDbHexArgsInvalid(t *testing.T) { + input := `{}` + + args := new(DbHexArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbHexArgsEmpty(t *testing.T) { + input := `[]` + + args := new(DbHexArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbHexArgsDatabaseType(t *testing.T) { + input := `[true, "keyval", "valval"]` + + args := new(DbHexArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbHexArgsKeyType(t *testing.T) { + input := `["dbval", 3, "valval"]` + + args := new(DbHexArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbHexArgsValType(t *testing.T) { + input := `["dbval", "keyval", {}]` + + args := new(DbHexArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbHexArgsDatabaseEmpty(t *testing.T) { + input := `["", "keyval", "valval"]` + + args := new(DbHexArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err.Error()) + } + + str := ExpectValidationError(args.requirements()) + if len(str) > 0 { + t.Error(str) + } +} + +func TestDbHexArgsKeyEmpty(t *testing.T) { + input := `["dbval", "", "valval"]` + + args := new(DbHexArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err.Error()) + } + + str := ExpectValidationError(args.requirements()) + if len(str) > 0 { + t.Error(str) + } +} + +func TestWhisperMessageArgs(t *testing.T) { + input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", + "topics": ["0x68656c6c6f20776f726c64"], + "payload":"0x68656c6c6f20776f726c64", + "ttl": "0x64", + "priority": "0x64"}]` + expected := new(WhisperMessageArgs) + expected.From = "0xc931d93e97ab07fe42d923478ba2465f2" + expected.To = "" + expected.Payload = "0x68656c6c6f20776f726c64" + expected.Priority = 100 + expected.Ttl = 100 + // expected.Topics = []string{"0x68656c6c6f20776f726c64"} + + args := new(WhisperMessageArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + if expected.Payload != args.Payload { + t.Errorf("Value shoud be %#v but is %#v", expected.Payload, args.Payload) + } + + if expected.Ttl != args.Ttl { + t.Errorf("Ttl shoud be %#v but is %#v", expected.Ttl, args.Ttl) + } + + if expected.Priority != args.Priority { + t.Errorf("Priority shoud be %#v but is %#v", expected.Priority, args.Priority) + } + + // if expected.Topics != args.Topics { + // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) + // } +} + +func TestWhisperMessageArgsInt(t *testing.T) { + input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", + "topics": ["0x68656c6c6f20776f726c64"], + "payload":"0x68656c6c6f20776f726c64", + "ttl": 12, + "priority": 16}]` + expected := new(WhisperMessageArgs) + expected.From = "0xc931d93e97ab07fe42d923478ba2465f2" + expected.To = "" + expected.Payload = "0x68656c6c6f20776f726c64" + expected.Priority = 16 + expected.Ttl = 12 + // expected.Topics = []string{"0x68656c6c6f20776f726c64"} + + args := new(WhisperMessageArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + if expected.Payload != args.Payload { + t.Errorf("Value shoud be %#v but is %#v", expected.Payload, args.Payload) + } + + if expected.Ttl != args.Ttl { + t.Errorf("Ttl shoud be %v but is %v", expected.Ttl, args.Ttl) + } + + if expected.Priority != args.Priority { + t.Errorf("Priority shoud be %v but is %v", expected.Priority, args.Priority) + } + + // if expected.Topics != args.Topics { + // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) + // } +} + +func TestWhisperMessageArgsInvalid(t *testing.T) { + input := `{}` + + args := new(WhisperMessageArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestWhisperMessageArgsEmpty(t *testing.T) { + input := `[]` + + args := new(WhisperMessageArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestWhisperMessageArgsTtlBool(t *testing.T) { + input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", + "topics": ["0x68656c6c6f20776f726c64"], + "payload":"0x68656c6c6f20776f726c64", + "ttl": true, + "priority": "0x64"}]` + args := new(WhisperMessageArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestWhisperMessageArgsPriorityBool(t *testing.T) { + input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", + "topics": ["0x68656c6c6f20776f726c64"], + "payload":"0x68656c6c6f20776f726c64", + "ttl": "0x12", + "priority": true}]` + args := new(WhisperMessageArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestFilterIdArgs(t *testing.T) { + input := `["0x7"]` + expected := new(FilterIdArgs) + expected.Id = 7 + + args := new(FilterIdArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Id != args.Id { + t.Errorf("Id shoud be %#v but is %#v", expected.Id, args.Id) + } +} + +func TestFilterIdArgsInvalid(t *testing.T) { + input := `{}` + + args := new(FilterIdArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestFilterIdArgsEmpty(t *testing.T) { + input := `[]` + + args := new(FilterIdArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestFilterIdArgsBool(t *testing.T) { + input := `[true]` + + args := new(FilterIdArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestWhisperFilterArgs(t *testing.T) { + input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": "0x34ag445g3455b34"}]` + expected := new(WhisperFilterArgs) + expected.To = "0x34ag445g3455b34" + expected.Topics = []string{"0x68656c6c6f20776f726c64"} + + args := new(WhisperFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + // if expected.Topics != args.Topics { + // t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + // } +} + +func TestWhisperFilterArgsInvalid(t *testing.T) { + input := `{}` + + args := new(WhisperFilterArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestWhisperFilterArgsEmpty(t *testing.T) { + input := `[]` + + args := new(WhisperFilterArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestWhisperFilterArgsToBool(t *testing.T) { + input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": false}]` + + args := new(WhisperFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestWhisperFilterArgsTopicInt(t *testing.T) { + input := `[{"topics": [6], "to": "0x34ag445g3455b34"}]` + + args := new(WhisperFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCompileArgs(t *testing.T) { + input := `["contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"]` + expected := new(CompileArgs) + expected.Source = `contract test { function multiply(uint a) returns(uint d) { return a * 7; } }` + + args := new(CompileArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Source != args.Source { + t.Errorf("Source shoud be %#v but is %#v", expected.Source, args.Source) + } +} + +func TestCompileArgsInvalid(t *testing.T) { + input := `{}` + + args := new(CompileArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCompileArgsEmpty(t *testing.T) { + input := `[]` + + args := new(CompileArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestCompileArgsBool(t *testing.T) { + input := `[false]` + + args := new(CompileArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestFilterStringArgs(t *testing.T) { + input := `["pending"]` + expected := new(FilterStringArgs) + expected.Word = "pending" + + args := new(FilterStringArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Word != args.Word { + t.Errorf("Word shoud be %#v but is %#v", expected.Word, args.Word) + } +} + +func TestFilterStringEmptyArgs(t *testing.T) { + input := `[]` + + args := new(FilterStringArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestFilterStringInvalidArgs(t *testing.T) { + input := `{}` + + args := new(FilterStringArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestFilterStringWordInt(t *testing.T) { + input := `[7]` + + args := new(FilterStringArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestFilterStringWordWrong(t *testing.T) { + input := `["foo"]` + + args := new(FilterStringArgs) + str := ExpectValidationError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestWhisperIdentityArgs(t *testing.T) { + input := `["0xc931d93e97ab07fe42d923478ba2465f283"]` + expected := new(WhisperIdentityArgs) + expected.Identity = "0xc931d93e97ab07fe42d923478ba2465f283" + + args := new(WhisperIdentityArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Identity != args.Identity { + t.Errorf("Identity shoud be %#v but is %#v", expected.Identity, args.Identity) + } +} + +func TestWhisperIdentityArgsInvalid(t *testing.T) { + input := `{}` + + args := new(WhisperIdentityArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestWhisperIdentityArgsEmpty(t *testing.T) { + input := `[]` + + args := new(WhisperIdentityArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestWhisperIdentityArgsInt(t *testing.T) { + input := `[4]` + + args := new(WhisperIdentityArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Errorf(str) + } +} + +func TestBlockNumIndexArgs(t *testing.T) { + input := `["0x29a", "0x0"]` + expected := new(BlockNumIndexArgs) + expected.BlockNumber = 666 + expected.Index = 0 + + args := new(BlockNumIndexArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } + + if expected.Index != args.Index { + t.Errorf("Index shoud be %#v but is %#v", expected.Index, args.Index) + } +} + +func TestBlockNumIndexArgsEmpty(t *testing.T) { + input := `[]` + + args := new(BlockNumIndexArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockNumIndexArgsInvalid(t *testing.T) { + input := `"foo"` + + args := new(BlockNumIndexArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockNumIndexArgsBlocknumInvalid(t *testing.T) { + input := `[{}, "0x1"]` + + args := new(BlockNumIndexArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockNumIndexArgsIndexInvalid(t *testing.T) { + input := `["0x29a", 1]` + + args := new(BlockNumIndexArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestHashIndexArgs(t *testing.T) { + input := `["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "0x1"]` + expected := new(HashIndexArgs) + expected.Hash = "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b" + expected.Index = 1 + + args := new(HashIndexArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Hash != args.Hash { + t.Errorf("Hash shoud be %#v but is %#v", expected.Hash, args.Hash) + } + + if expected.Index != args.Index { + t.Errorf("Index shoud be %#v but is %#v", expected.Index, args.Index) + } +} + +func TestHashIndexArgsEmpty(t *testing.T) { + input := `[]` + + args := new(HashIndexArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestHashIndexArgsInvalid(t *testing.T) { + input := `{}` + + args := new(HashIndexArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestHashIndexArgsInvalidHash(t *testing.T) { + input := `[7, "0x1"]` + + args := new(HashIndexArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestHashIndexArgsInvalidIndex(t *testing.T) { + input := `["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", false]` + + args := new(HashIndexArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestSubmitWorkArgs(t *testing.T) { + input := `["0x0000000000000001", "0x1234567890abcdef1234567890abcdef", "0xD1GE5700000000000000000000000000"]` + expected := new(SubmitWorkArgs) + expected.Nonce = 1 + expected.Header = "0x1234567890abcdef1234567890abcdef" + expected.Digest = "0xD1GE5700000000000000000000000000" + + args := new(SubmitWorkArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Nonce != args.Nonce { + t.Errorf("Nonce shoud be %d but is %d", expected.Nonce, args.Nonce) + } + + if expected.Header != args.Header { + t.Errorf("Header shoud be %#v but is %#v", expected.Header, args.Header) + } + + if expected.Digest != args.Digest { + t.Errorf("Digest shoud be %#v but is %#v", expected.Digest, args.Digest) + } +} + +func TestSubmitWorkArgsInvalid(t *testing.T) { + input := `{}` + + args := new(SubmitWorkArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestSubmitWorkArgsEmpty(t *testing.T) { + input := `[]` + + args := new(SubmitWorkArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestSubmitWorkArgsNonceInt(t *testing.T) { + input := `[1, "0x1234567890abcdef1234567890abcdef", "0xD1GE5700000000000000000000000000"]` + + args := new(SubmitWorkArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} +func TestSubmitWorkArgsHeaderInt(t *testing.T) { + input := `["0x0000000000000001", 1, "0xD1GE5700000000000000000000000000"]` + + args := new(SubmitWorkArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} +func TestSubmitWorkArgsDigestInt(t *testing.T) { + input := `["0x0000000000000001", "0x1234567890abcdef1234567890abcdef", 1]` + + args := new(SubmitWorkArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockHeightFromJsonInvalid(t *testing.T) { + var num int64 + var msg json.RawMessage = []byte(`}{`) + str := ExpectDecodeParamError(blockHeightFromJson(msg, &num)) + if len(str) > 0 { + t.Error(str) + } +} diff --git a/rpc/http.go b/rpc/http.go new file mode 100644 index 000000000..f15d557ad --- /dev/null +++ b/rpc/http.go @@ -0,0 +1,127 @@ +package rpc + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/xeth" + "github.com/rs/cors" +) + +var rpclogger = logger.NewLogger("RPC") + +const ( + jsonrpcver = "2.0" + maxSizeReqLength = 1024 * 1024 // 1MB +) + +func Start(pipe *xeth.XEth, config RpcConfig) error { + l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort)) + if err != nil { + rpclogger.Errorf("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err) + return err + } + + var handler http.Handler + if len(config.CorsDomain) > 0 { + var opts cors.Options + opts.AllowedMethods = []string{"POST"} + opts.AllowedOrigins = []string{config.CorsDomain} + + c := cors.New(opts) + handler = c.Handler(JSONRPC(pipe)) + } else { + handler = JSONRPC(pipe) + } + + go http.Serve(l, handler) + + return nil +} + +// JSONRPC returns a handler that implements the Ethereum JSON-RPC API. +func JSONRPC(pipe *xeth.XEth) http.Handler { + api := NewEthereumApi(pipe) + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + + // Limit request size to resist DoS + if req.ContentLength > maxSizeReqLength { + jsonerr := &RpcErrorObject{-32700, "Request too large"} + send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) + return + } + + // Read request body + defer req.Body.Close() + body, err := ioutil.ReadAll(req.Body) + if err != nil { + jsonerr := &RpcErrorObject{-32700, "Could not read request body"} + send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) + } + + // Try to parse the request as a single + var reqSingle RpcRequest + if err := json.Unmarshal(body, &reqSingle); err == nil { + response := RpcResponse(api, &reqSingle) + send(w, &response) + return + } + + // Try to parse the request to batch + var reqBatch []RpcRequest + if err := json.Unmarshal(body, &reqBatch); err == nil { + // Build response batch + resBatch := make([]*interface{}, len(reqBatch)) + for i, request := range reqBatch { + response := RpcResponse(api, &request) + resBatch[i] = response + } + send(w, resBatch) + return + } + + // Not a batch or single request, error + jsonerr := &RpcErrorObject{-32600, "Could not decode request"} + send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) + }) +} + +func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} { + var reply, response interface{} + reserr := api.GetRequestReply(request, &reply) + switch reserr.(type) { + case nil: + response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply} + case *NotImplementedError: + jsonerr := &RpcErrorObject{-32601, reserr.Error()} + response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} + case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: + jsonerr := &RpcErrorObject{-32602, reserr.Error()} + response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} + default: + jsonerr := &RpcErrorObject{-32603, reserr.Error()} + response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} + } + + rpclogger.DebugDetailf("Generated response: %T %s", response, response) + return &response +} + +func send(writer io.Writer, v interface{}) (n int, err error) { + var payload []byte + payload, err = json.MarshalIndent(v, "", "\t") + if err != nil { + rpclogger.Fatalln("Error marshalling JSON", err) + return 0, err + } + rpclogger.DebugDetailf("Sending payload: %s", payload) + + return writer.Write(payload) +} diff --git a/rpc/jeth.go b/rpc/jeth.go new file mode 100644 index 000000000..e83212bb5 --- /dev/null +++ b/rpc/jeth.go @@ -0,0 +1,58 @@ +package rpc + +import ( + "encoding/json" + "fmt" + // "fmt" + "github.com/ethereum/go-ethereum/jsre" + "github.com/robertkrimen/otto" +) + +type Jeth struct { + ethApi *EthereumApi + toVal func(interface{}) otto.Value + re *jsre.JSRE +} + +func NewJeth(ethApi *EthereumApi, toVal func(interface{}) otto.Value, re *jsre.JSRE) *Jeth { + return &Jeth{ethApi, toVal, re} +} + +func (self *Jeth) err(code int, msg string, id interface{}) (response otto.Value) { + rpcerr := &RpcErrorObject{code, msg} + self.re.Set("ret_jsonrpc", jsonrpcver) + self.re.Set("ret_id", id) + self.re.Set("ret_error", rpcerr) + response, _ = self.re.Run(` + ret_response = { jsonrpc: ret_jsonrpc, id: ret_id, error: ret_error }; + `) + return +} + +func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { + reqif, err := call.Argument(0).Export() + if err != nil { + return self.err(-32700, err.Error(), nil) + } + + jsonreq, err := json.Marshal(reqif) + + var req RpcRequest + err = json.Unmarshal(jsonreq, &req) + + var respif interface{} + err = self.ethApi.GetRequestReply(&req, &respif) + if err != nil { + fmt.Printf("error: %s\n", err) + return self.err(-32603, err.Error(), req.Id) + } + self.re.Set("ret_jsonrpc", jsonrpcver) + self.re.Set("ret_id", req.Id) + + res, _ := json.Marshal(respif) + self.re.Set("ret_result", string(res)) + response, err = self.re.Run(` + ret_response = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; + `) + return +} diff --git a/rpc/responses.go b/rpc/responses.go new file mode 100644 index 000000000..3d1687cb6 --- /dev/null +++ b/rpc/responses.go @@ -0,0 +1,257 @@ +package rpc + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" +) + +type BlockRes struct { + fullTx bool + + BlockNumber *hexnum `json:"number"` + BlockHash *hexdata `json:"hash"` + ParentHash *hexdata `json:"parentHash"` + Nonce *hexdata `json:"nonce"` + Sha3Uncles *hexdata `json:"sha3Uncles"` + LogsBloom *hexdata `json:"logsBloom"` + TransactionRoot *hexdata `json:"transactionsRoot"` + StateRoot *hexdata `json:"stateRoot"` + Miner *hexdata `json:"miner"` + Difficulty *hexnum `json:"difficulty"` + TotalDifficulty *hexnum `json:"totalDifficulty"` + Size *hexnum `json:"size"` + ExtraData *hexdata `json:"extraData"` + GasLimit *hexnum `json:"gasLimit"` + MinGasPrice *hexnum `json:"minGasPrice"` + GasUsed *hexnum `json:"gasUsed"` + UnixTimestamp *hexnum `json:"timestamp"` + Transactions []*TransactionRes `json:"transactions"` + Uncles []*hexdata `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"` + MinGasPrice *hexnum `json:"minGasPrice"` + 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.MinGasPrice = b.MinGasPrice + ext.GasUsed = b.GasUsed + ext.UnixTimestamp = b.UnixTimestamp + ext.Transactions = b.Transactions + ext.Uncles = b.Uncles + 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"` + MinGasPrice *hexnum `json:"minGasPrice"` + 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.MinGasPrice = b.MinGasPrice + 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 = b.Uncles + return json.Marshal(ext) + } +} + +func NewBlockRes(block *types.Block, fullTx bool) *BlockRes { + // TODO respect fullTx flag + + 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.MinGasPrice = + 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([]*hexdata, len(block.Uncles())) + for i, uncle := range block.Uncles() { + res.Uncles[i] = newHexData(uncle.Hash()) + } + + 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 { + 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 FilterLogRes struct { +// Hash string `json:"hash"` +// Address string `json:"address"` +// Data string `json:"data"` +// BlockNumber string `json:"blockNumber"` +// TransactionHash string `json:"transactionHash"` +// BlockHash string `json:"blockHash"` +// TransactionIndex string `json:"transactionIndex"` +// LogIndex string `json:"logIndex"` +// } + +// type FilterWhisperRes struct { +// Hash string `json:"hash"` +// From string `json:"from"` +// To string `json:"to"` +// Expiry string `json:"expiry"` +// Sent string `json:"sent"` +// Ttl string `json:"ttl"` +// Topics string `json:"topics"` +// Payload string `json:"payload"` +// WorkProved string `json:"workProved"` +// } + +type LogRes struct { + Address *hexdata `json:"address"` + Topics []*hexdata `json:"topics"` + Data *hexdata `json:"data"` + BlockNumber *hexnum `json:"blockNumber"` + Hash *hexdata `json:"hash"` + 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()) + + return l +} + +func NewLogsRes(logs state.Logs) (ls []LogRes) { + ls = make([]LogRes, len(logs)) + + for i, log := range logs { + ls[i] = NewLogRes(log) + } + + return +} diff --git a/rpc/responses_test.go b/rpc/responses_test.go new file mode 100644 index 000000000..2ec6d9d15 --- /dev/null +++ b/rpc/responses_test.go @@ -0,0 +1,219 @@ +package rpc + +import ( + "encoding/json" + "fmt" + "math/big" + "regexp" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" +) + +const ( + reHash = `"0x[0-9a-f]{64}"` // 32 bytes + reHashOpt = `"(0x[0-9a-f]{64})"|null` // 32 bytes or null + reAddress = `"0x[0-9a-f]{40}"` // 20 bytes + reAddressOpt = `"0x[0-9a-f]{40}"|null` // 20 bytes or null + reNum = `"0x([1-9a-f][0-9a-f]{0,15})|0"` // must not have left-padded zeros + reNumNonZero = `"0x([1-9a-f][0-9a-f]{0,15})"` // non-zero required must not have left-padded zeros + reNumOpt = `"0x([1-9a-f][0-9a-f]{0,15})|0"|null` // must not have left-padded zeros or null + reData = `"0x[0-9a-f]*"` // can be "empty" + // reListHash = `[("\w":"0x[0-9a-f]{64}",?)*]` + // reListObj = `[("\w":(".+"|null),?)*]` +) + +func TestNewBlockRes(t *testing.T) { + parentHash := common.HexToHash("0x01") + coinbase := common.HexToAddress("0x01") + root := common.HexToHash("0x01") + difficulty := common.Big1 + nonce := uint64(1) + extra := "" + block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, extra) + tests := map[string]string{ + "number": reNum, + "hash": reHash, + "parentHash": reHash, + "nonce": reData, + "sha3Uncles": reHash, + "logsBloom": reData, + "transactionsRoot": reHash, + "stateRoot": reHash, + "miner": reAddress, + "difficulty": `"0x1"`, + "totalDifficulty": reNum, + "size": reNumNonZero, + "extraData": reData, + "gasLimit": reNum, + // "minGasPrice": "0x", + "gasUsed": reNum, + "timestamp": reNum, + // "transactions": reListHash, + // "uncles": reListHash, + } + + to := common.HexToAddress("0x02") + amount := big.NewInt(1) + gasAmount := big.NewInt(1) + gasPrice := big.NewInt(1) + data := []byte{1, 2, 3} + tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data) + + v := NewBlockRes(block, false) + v.Transactions = make([]*TransactionRes, 1) + v.Transactions[0] = NewTransactionRes(tx) + j, _ := json.Marshal(v) + + for k, re := range tests { + match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) + if !match { + t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j)) + } + } +} + +func TestNewBlockResWithTrans(t *testing.T) { + parentHash := common.HexToHash("0x01") + coinbase := common.HexToAddress("0x01") + root := common.HexToHash("0x01") + difficulty := common.Big1 + nonce := uint64(1) + extra := "" + block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, extra) + tests := map[string]string{ + "number": reNum, + "hash": reHash, + "parentHash": reHash, + "nonce": reData, + "sha3Uncles": reHash, + "logsBloom": reData, + "transactionsRoot": reHash, + "stateRoot": reHash, + "miner": reAddress, + "difficulty": `"0x1"`, + "totalDifficulty": reNum, + "size": reNumNonZero, + "extraData": reData, + "gasLimit": reNum, + // "minGasPrice": "0x", + "gasUsed": reNum, + "timestamp": reNum, + // "transactions": `[{.*}]`, + // "uncles": reListHash, + } + + to := common.HexToAddress("0x02") + amount := big.NewInt(1) + gasAmount := big.NewInt(1) + gasPrice := big.NewInt(1) + data := []byte{1, 2, 3} + tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data) + + v := NewBlockRes(block, true) + v.Transactions = make([]*TransactionRes, 1) + v.Transactions[0] = NewTransactionRes(tx) + j, _ := json.Marshal(v) + + for k, re := range tests { + match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) + if !match { + t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j)) + } + } +} + +func TestNewTransactionRes(t *testing.T) { + to := common.HexToAddress("0x02") + amount := big.NewInt(1) + gasAmount := big.NewInt(1) + gasPrice := big.NewInt(1) + data := []byte{1, 2, 3} + tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data) + + tests := map[string]string{ + "hash": reHash, + "nonce": reNum, + "blockHash": reHashOpt, + "blockNum": reNumOpt, + "transactionIndex": reNumOpt, + "from": reAddress, + "to": reAddressOpt, + "value": reNum, + "gas": reNum, + "gasPrice": reNum, + "input": reData, + } + + v := NewTransactionRes(tx) + v.BlockHash = newHexData(common.HexToHash("0x030201")) + v.BlockNumber = newHexNum(5) + v.TxIndex = newHexNum(0) + j, _ := json.Marshal(v) + for k, re := range tests { + match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) + if !match { + t.Error(fmt.Sprintf("`%s` output json does not match format %s. Source %s", k, re, j)) + } + } + +} + +func TestNewLogRes(t *testing.T) { + log := makeStateLog(0) + tests := map[string]string{ + "address": reAddress, + // "topics": "[.*]" + "data": reData, + "blockNumber": reNum, + // "hash": reHash, + // "logIndex": reNum, + // "blockHash": reHash, + // "transactionHash": reHash, + "transactionIndex": reNum, + } + + v := NewLogRes(log) + j, _ := json.Marshal(v) + + for k, re := range tests { + match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j)) + if !match { + t.Error(fmt.Sprintf("`%s` output json does not match format %s. Got %s", k, re, j)) + } + } + +} + +func TestNewLogsRes(t *testing.T) { + logs := make([]state.Log, 3) + logs[0] = makeStateLog(1) + logs[1] = makeStateLog(2) + logs[2] = makeStateLog(3) + tests := map[string]string{} + + v := NewLogsRes(logs) + j, _ := json.Marshal(v) + + for k, re := range tests { + match, _ := regexp.MatchString(fmt.Sprintf(`[{.*"%s":%s.*}]`, k, re), string(j)) + if !match { + t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j)) + } + } + +} + +func makeStateLog(num int) state.Log { + address := common.HexToAddress("0x0") + data := []byte{1, 2, 3} + number := uint64(num) + topics := make([]common.Hash, 3) + topics = append(topics, common.HexToHash("0x00")) + topics = append(topics, common.HexToHash("0x10")) + topics = append(topics, common.HexToHash("0x20")) + log := state.NewLog(address, topics, data, number) + return log +} diff --git a/rpc/types.go b/rpc/types.go new file mode 100644 index 000000000..806c9c8a4 --- /dev/null +++ b/rpc/types.go @@ -0,0 +1,269 @@ +/* + This file is part of go-ethereum + + go-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + go-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +package rpc + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type hexdata struct { + data []byte + isNil bool +} + +func (d *hexdata) String() string { + return "0x" + common.Bytes2Hex(d.data) +} + +func (d *hexdata) MarshalJSON() ([]byte, error) { + if d.isNil { + return json.Marshal(nil) + } + return json.Marshal(d.String()) +} + +func (d *hexdata) UnmarshalJSON(b []byte) (err error) { + d.data = common.FromHex(string(b)) + return nil +} + +func newHexData(input interface{}) *hexdata { + d := new(hexdata) + + if input == nil { + d.data = nil + 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, 8) + binary.BigEndian.PutUint16(buff, input) + d.data = buff + case int32: + d.data = big.NewInt(int64(input)).Bytes() + case uint32: + buff := make([]byte, 8) + binary.BigEndian.PutUint32(buff, input) + d.data = buff + case string: // hexstring + d.data = common.Big(input).Bytes() + default: + d.data = nil + } + + 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 (d *hexnum) UnmarshalJSON(b []byte) (err error) { + d.data = common.FromHex(string(b)) + return nil +} + +func newHexNum(input interface{}) *hexnum { + d := new(hexnum) + + d.data = newHexData(input).data + + return d +} + +type RpcConfig struct { + ListenAddress string + ListenPort uint + CorsDomain string +} + +type InvalidTypeError struct { + method string + msg string +} + +func (e *InvalidTypeError) Error() string { + return fmt.Sprintf("invalid type on field %s: %s", e.method, e.msg) +} + +func NewInvalidTypeError(method, msg string) *InvalidTypeError { + return &InvalidTypeError{ + method: method, + msg: msg, + } +} + +type InsufficientParamsError struct { + have int + want int +} + +func (e *InsufficientParamsError) Error() string { + return fmt.Sprintf("insufficient params, want %d have %d", e.want, e.have) +} + +func NewInsufficientParamsError(have int, want int) *InsufficientParamsError { + return &InsufficientParamsError{ + have: have, + want: want, + } +} + +type NotImplementedError struct { + Method string +} + +func (e *NotImplementedError) Error() string { + return fmt.Sprintf("%s method not implemented", e.Method) +} + +func NewNotImplementedError(method string) *NotImplementedError { + return &NotImplementedError{ + Method: method, + } +} + +type DecodeParamError struct { + err string +} + +func (e *DecodeParamError) Error() string { + return fmt.Sprintf("could not decode, %s", e.err) + +} + +func NewDecodeParamError(errstr string) error { + return &DecodeParamError{ + err: errstr, + } +} + +type ValidationError struct { + ParamName string + msg string +} + +func (e *ValidationError) Error() string { + return fmt.Sprintf("%s not valid, %s", e.ParamName, e.msg) +} + +func NewValidationError(param string, msg string) error { + return &ValidationError{ + ParamName: param, + msg: msg, + } +} + +type RpcRequest struct { + Id interface{} `json:"id"` + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params json.RawMessage `json:"params"` +} + +type RpcSuccessResponse struct { + Id interface{} `json:"id"` + Jsonrpc string `json:"jsonrpc"` + Result interface{} `json:"result"` +} + +type RpcErrorResponse struct { + Id interface{} `json:"id"` + Jsonrpc string `json:"jsonrpc"` + Error *RpcErrorObject `json:"error"` +} + +type RpcErrorObject struct { + Code int `json:"code"` + Message string `json:"message"` + // Data interface{} `json:"data"` +} diff --git a/rpc/types_test.go b/rpc/types_test.go new file mode 100644 index 000000000..91f0152dc --- /dev/null +++ b/rpc/types_test.go @@ -0,0 +1,50 @@ +package rpc + +import ( + "testing" +) + +func TestInvalidTypeError(t *testing.T) { + err := NewInvalidTypeError("testField", "not string") + expected := "invalid type on field testField: not string" + + if err.Error() != expected { + t.Error(err.Error()) + } +} + +func TestInsufficientParamsError(t *testing.T) { + err := NewInsufficientParamsError(0, 1) + expected := "insufficient params, want 1 have 0" + + if err.Error() != expected { + t.Error(err.Error()) + } +} + +func TestNotImplementedError(t *testing.T) { + err := NewNotImplementedError("foo") + expected := "foo method not implemented" + + if err.Error() != expected { + t.Error(err.Error()) + } +} + +func TestDecodeParamError(t *testing.T) { + err := NewDecodeParamError("foo") + expected := "could not decode, foo" + + if err.Error() != expected { + t.Error(err.Error()) + } +} + +func TestValidationError(t *testing.T) { + err := NewValidationError("foo", "should be `bar`") + expected := "foo not valid, should be `bar`" + + if err.Error() != expected { + t.Error(err.Error()) + } +} |