diff options
Diffstat (limited to 'rpc')
-rw-r--r-- | rpc/api.go | 535 | ||||
-rw-r--r-- | rpc/api_test.go | 102 | ||||
-rw-r--r-- | rpc/args.go | 792 | ||||
-rw-r--r-- | rpc/args_test.go | 753 | ||||
-rw-r--r-- | rpc/http.go | 101 | ||||
-rw-r--r-- | rpc/jeth.go | 54 | ||||
-rw-r--r-- | rpc/messages.go | 108 | ||||
-rw-r--r-- | rpc/messages_test.go | 41 | ||||
-rw-r--r-- | rpc/responses.go | 243 |
9 files changed, 2729 insertions, 0 deletions
diff --git a/rpc/api.go b/rpc/api.go new file mode 100644 index 000000000..4bc199176 --- /dev/null +++ b/rpc/api.go @@ -0,0 +1,535 @@ +package rpc + +import ( + "encoding/json" + "math/big" + "path" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/xeth" +) + +type EthereumApi struct { + eth *xeth.XEth + xethMu sync.RWMutex + db common.Database +} + +func NewEthereumApi(xeth *xeth.XEth, dataDir string) *EthereumApi { + // What about when dataDir is empty? + db, err := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps")) + if err != nil { + panic(err) + } + api := &EthereumApi{ + eth: xeth, + db: db, + } + + 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) Close() { + api.db.Close() +} + +func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { + // Spec at https://github.com/ethereum/wiki/wiki/Generic-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().Backend().Version() + case "net_version": + *reply = string(api.xeth().Backend().ProtocolVersion()) + case "net_listening": + *reply = api.xeth().IsListening() + case "net_peerCount": + v := api.xeth().PeerCount() + *reply = common.ToHex(big.NewInt(int64(v)).Bytes()) + 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 := api.xeth().DefaultGas() + *reply = common.ToHex(v.Bytes()) + case "eth_accounts": + *reply = api.xeth().Accounts() + case "eth_blockNumber": + v := api.xeth().Backend().ChainManager().CurrentBlock().Number() + *reply = common.ToHex(v.Bytes()) + case "eth_getBalance": + args := new(GetBalanceArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + if err := args.requirements(); err != nil { + return err + } + + 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 + } + + if err := args.requirements(); 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 + } + if err := args.requirements(); err != nil { + return err + } + + state := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address) + value := state.StorageString(args.Key) + + *reply = common.Bytes2Hex(value.Bytes()) + case "eth_getTransactionCount": + args := new(GetTxCountArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + err := args.requirements() + if err != nil { + return err + } + + *reply = api.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address) + case "eth_getBlockTransactionCountByHash": + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := NewBlockRes(api.xeth().EthBlockByHash(args.BlockHash)) + *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes()) + case "eth_getBlockTransactionCountByNumber": + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := NewBlockRes(api.xeth().EthBlockByNumber(args.BlockNumber)) + *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes()) + case "eth_getUncleCountByBlockHash": + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByHash(args.BlockHash) + br := NewBlockRes(block) + *reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes()) + case "eth_getUncleCountByBlockNumber": + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + block := api.xeth().EthBlockByNumber(args.BlockNumber) + br := NewBlockRes(block) + *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 + } + if err := args.requirements(); err != nil { + return err + } + *reply = api.xethAtStateNum(args.BlockNumber).CodeAt(args.Address) + case "eth_sendTransaction", "eth_transact": + args := new(NewTxArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + if err := args.requirements(); 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(NewTxArgs) + 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) + br.fullTx = 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) + br.fullTx = 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 := api.xeth().EthTransactionByHash(args.Hash) + if tx != nil { + *reply = NewTransactionRes(tx) + } + 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) + br.fullTx = 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) + v.fullTx = 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)) + + if args.Index > int64(len(br.Uncles)) || args.Index < 0 { + return NewValidationError("Index", "does not exist") + } + + uhash := br.Uncles[args.Index].Hex() + uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash)) + + *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) + v.fullTx = true + + if args.Index > int64(len(v.Uncles)) || args.Index < 0 { + return NewValidationError("Index", "does not exist") + } + + uhash := v.Uncles[args.Index].Hex() + uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash)) + + *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 + } + + opts := toFilterOptions(args) + id := api.xeth().RegisterFilter(opts) + *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 + } + if err := args.requirements(); 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 + } + opts := toFilterOptions(args) + *reply = NewLogsRes(api.xeth().AllLogs(opts)) + 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, args.Digest, 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.db.Put([]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.db.Get([]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.db.Put([]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.db.Get([]byte(args.Database + args.Key)) + *reply = common.ToHex(res) + 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 +} + +func toFilterOptions(options *BlockFilterArgs) *core.FilterOptions { + var opts core.FilterOptions + + // Convert optional address slice/string to byte slice + if str, ok := options.Address.(string); ok { + opts.Address = []common.Address{common.HexToAddress(str)} + } else if slice, ok := options.Address.([]interface{}); ok { + bslice := make([]common.Address, len(slice)) + for i, addr := range slice { + if saddr, ok := addr.(string); ok { + bslice[i] = common.HexToAddress(saddr) + } + } + opts.Address = bslice + } + + opts.Earliest = options.Earliest + opts.Latest = options.Latest + + topics := make([][]common.Hash, len(options.Topics)) + for i, topicDat := range options.Topics { + if slice, ok := topicDat.([]interface{}); ok { + topics[i] = make([]common.Hash, len(slice)) + for j, topic := range slice { + topics[i][j] = common.HexToHash(topic.(string)) + } + } else if str, ok := topicDat.(string); ok { + topics[i] = []common.Hash{common.HexToHash(str)} + } + } + opts.Topics = topics + + return &opts +} + +/* + Work() chan<- *types.Block + SetWorkCh(chan<- Work) + Stop() + Start() + Rate() uint64 +*/ diff --git a/rpc/api_test.go b/rpc/api_test.go new file mode 100644 index 000000000..a00c2f3f1 --- /dev/null +++ b/rpc/api_test.go @@ -0,0 +1,102 @@ +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, "") + 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 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..5b655024c --- /dev/null +++ b/rpc/args.go @@ -0,0 +1,792 @@ +package rpc + +import ( + "bytes" + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +func blockHeight(raw interface{}, number *int64) (err 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 NewDecodeParamError("BlockNumber is not a string") + } + + switch str { + case "latest": + *number = -1 + case "pending": + *number = -2 + default: + *number = common.String2Big(str).Int64() + } + + return nil +} + +type GetBlockByHashArgs struct { + BlockHash string + IncludeTxs bool +} + +func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + argstr, ok := obj[0].(string) + if !ok { + return NewDecodeParamError("BlockHash not a string") + } + args.BlockHash = argstr + + if len(obj) > 1 { + 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{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + if v, ok := obj[0].(float64); ok { + args.BlockNumber = int64(v) + } else { + args.BlockNumber = common.Big(obj[0].(string)).Int64() + } + + if len(obj) > 1 { + 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, To, Value, Gas, GasPrice, 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()) + } + + // var ok bool + args.From = ext.From + args.To = ext.To + args.Value = common.String2Big(ext.Value) + args.Gas = common.String2Big(ext.Gas) + args.GasPrice = common.String2Big(ext.GasPrice) + args.Data = ext.Data + + // Check for optional BlockNumber param + if len(obj) > 1 { + var raw interface{} + if err = json.Unmarshal(obj[1], &raw); err != nil { + return NewDecodeParamError(err.Error()) + } + + if err := blockHeight(raw, &args.BlockNumber); err != nil { + return err + } + } + + return nil +} + +func (args *NewTxArgs) requirements() error { + if len(args.From) == 0 { + return NewValidationError("From", "Is required") + } + 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 NewDecodeParamError("Address is not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } + + return nil +} + +func (args *GetStorageArgs) requirements() error { + if len(args.Address) == 0 { + return NewValidationError("Address", "cannot be blank") + } + 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 NewDecodeParamError("Address is not a string") + } + args.Address = addstr + + keystr, ok := obj[1].(string) + if !ok { + return NewDecodeParamError("Key is not a string") + } + args.Key = keystr + + if len(obj) > 2 { + if err := blockHeight(obj[2], &args.BlockNumber); err != nil { + return err + } + } + + return nil +} + +func (args *GetStorageAtArgs) requirements() error { + if len(args.Address) == 0 { + return NewValidationError("Address", "cannot be blank") + } + + if len(args.Key) == 0 { + return NewValidationError("Key", "cannot be blank") + } + 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 NewDecodeParamError("Address is not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } + + return nil +} + +func (args *GetTxCountArgs) requirements() error { + if len(args.Address) == 0 { + return NewValidationError("Address", "cannot be blank") + } + 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 NewDecodeParamError("Address is not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } + + return nil +} + +func (args *GetBalanceArgs) requirements() error { + if len(args.Address) == 0 { + return NewValidationError("Address", "cannot be blank") + } + 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 NewDecodeParamError("Address is not a string") + } + args.Address = addstr + + if len(obj) > 1 { + if err := blockHeight(obj[1], &args.BlockNumber); err != nil { + return err + } + } + + return nil +} + +func (args *GetDataArgs) requirements() error { + if len(args.Address) == 0 { + return NewValidationError("Address", "cannot be blank") + } + return nil +} + +type BlockNumIndexArgs struct { + BlockNumber int64 + Index int64 +} + +func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + arg0, ok := obj[0].(string) + if !ok { + return NewDecodeParamError("BlockNumber is not string") + } + args.BlockNumber = common.Big(arg0).Int64() + + if len(obj) > 1 { + arg1, ok := obj[1].(string) + if !ok { + return NewDecodeParamError("Index not a string") + } + args.Index = common.Big(arg1).Int64() + } + + return nil +} + +type HashIndexArgs struct { + Hash string + Index int64 +} + +func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + arg0, ok := obj[0].(string) + if !ok { + return NewDecodeParamError("Hash not a string") + } + args.Hash = arg0 + + if len(obj) > 1 { + arg1, ok := obj[1].(string) + if !ok { + return NewDecodeParamError("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{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + args.Data = obj[0].(string) + + return nil +} + +type BlockFilterArgs struct { + Earliest int64 + Latest int64 + Address interface{} + Topics []interface{} + Skip int + Max int +} + +func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + FromBlock interface{} `json:"fromBlock"` + ToBlock interface{} `json:"toBlock"` + Limit string `json:"limit"` + Offset string `json:"offset"` + Address string `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) + } + + fromstr, ok := obj[0].FromBlock.(string) + if !ok { + return NewDecodeParamError("FromBlock is not a string") + } + + switch fromstr { + case "latest": + args.Earliest = -1 + default: + args.Earliest = int64(common.Big(obj[0].FromBlock.(string)).Int64()) + } + + tostr, ok := obj[0].ToBlock.(string) + if !ok { + return NewDecodeParamError("ToBlock is not a string") + } + + switch tostr { + case "latest": + args.Latest = -1 + case "pending": + args.Latest = -2 + default: + args.Latest = int64(common.Big(obj[0].ToBlock.(string)).Int64()) + } + + args.Max = int(common.Big(obj[0].Limit).Int64()) + args.Skip = int(common.Big(obj[0].Offset).Int64()) + args.Address = obj[0].Address + args.Topics = obj[0].Topics + + 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 NewDecodeParamError("Database is not a string") + } + args.Database = objstr + + if objstr, ok = obj[1].(string); !ok { + return NewDecodeParamError("Key is not a string") + } + args.Key = objstr + + if len(obj) > 2 { + objstr, ok = obj[2].(string) + if !ok { + return NewDecodeParamError("Value is 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 NewDecodeParamError("Database is not a string") + } + args.Database = objstr + + if objstr, ok = obj[1].(string); !ok { + return NewDecodeParamError("Key is not a string") + } + args.Key = objstr + + if len(obj) > 2 { + objstr, ok = obj[2].(string) + if !ok { + return NewDecodeParamError("Value is 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 string + Ttl string + } + + 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 + args.Priority = uint32(common.Big(obj[0].Priority).Int64()) + args.Ttl = uint32(common.Big(obj[0].Ttl).Int64()) + + return nil +} + +type CompileArgs struct { + Source string +} + +func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) > 0 { + args.Source = obj[0].(string) + } + + return nil +} + +type FilterStringArgs struct { + Word string +} + +func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&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 NewDecodeParamError("Filter is not a string") + } + args.Word = argstr + + return nil +} + +func (args *FilterStringArgs) requirements() error { + switch args.Word { + case "latest", "pending": + break + default: + return NewValidationError("Word", "Must be `latest` or `pending`") + } + return nil +} + +type FilterIdArgs struct { + Id int +} + +func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + args.Id = int(common.Big(obj[0]).Int64()) + + return nil +} + +type WhisperIdentityArgs struct { + Identity string +} + +func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + args.Identity = obj[0] + + 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 string + From string + Topics []string + } + + if err = json.Unmarshal(b, &obj); err != nil { + return NewDecodeParamError(err.Error()) + } + + if len(obj) < 1 { + return NewInsufficientParamsError(len(obj), 1) + } + + args.To = obj[0].To + args.From = obj[0].From + args.Topics = obj[0].Topics + + return nil +} + +type SubmitWorkArgs struct { + Nonce uint64 + Header common.Hash + Digest common.Hash +} + +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 NewDecodeParamError("Nonce is not a string") + } + + args.Nonce = common.String2Big(objstr).Uint64() + if objstr, ok = obj[1].(string); !ok { + return NewDecodeParamError("Header is not a string") + } + + args.Header = common.HexToHash(objstr) + + if objstr, ok = obj[2].(string); !ok { + return NewDecodeParamError("Digest is not a string") + } + + args.Digest = common.HexToHash(objstr) + + return nil +} diff --git a/rpc/args_test.go b/rpc/args_test.go new file mode 100644 index 000000000..5cbafd4b2 --- /dev/null +++ b/rpc/args_test.go @@ -0,0 +1,753 @@ +package rpc + +import ( + "bytes" + "encoding/json" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +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 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 err := args.requirements(); 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 err := args.requirements(); 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 TestGetBalanceEmptyArgs(t *testing.T) { + input := `[]` + + args := new(GetBalanceArgs) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } + +} + +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 TestGetBlockByHashEmpty(t *testing.T) { + input := `[]` + + args := new(GetBlockByHashArgs) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +func TestGetBlockByNumberArgs(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("BlockHash 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) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +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 TestNewTxArgsBlockInt(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}, 5]` + expected := new(NewTxArgs) + expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155" + expected.BlockNumber = big.NewInt(5).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.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestNewTxArgsEmpty(t *testing.T) { + input := `[]` + + args := new(NewTxArgs) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +func TestNewTxArgsReqs(t *testing.T) { + args := new(NewTxArgs) + args.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155" + + err := args.requirements() + switch err.(type) { + case nil: + break + default: + t.Errorf("Get %T", err) + } +} + +func TestNewTxArgsReqsFromBlank(t *testing.T) { + args := new(NewTxArgs) + args.From = "" + + err := args.requirements() + switch err.(type) { + case nil: + t.Error("Expected error but didn't get one") + case *ValidationError: + break + default: + t.Error("Wrong type of error") + } +} + +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 err := args.requirements(); 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 TestGetStorageEmptyArgs(t *testing.T) { + input := `[]` + + args := new(GetStorageArgs) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +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 err := args.requirements(); 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) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +func TestGetTxCountArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` + expected := new(GetTxCountArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = -1 + + args := new(GetTxCountArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); 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) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +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 err := args.requirements(); 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 TestGetDataEmptyArgs(t *testing.T) { + input := `[]` + + args := new(GetDataArgs) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +func TestBlockFilterArgs(t *testing.T) { + input := `[{ + "fromBlock": "0x1", + "toBlock": "0x2", + "limit": "0x3", + "offset": "0x0", + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": ["0x12341234"]}]` + expected := new(BlockFilterArgs) + expected.Earliest = 1 + expected.Latest = 2 + expected.Max = 3 + expected.Skip = 0 + expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" + // expected.Topics = []string{"0x12341234"} + + 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 != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + // if expected.Topics != args.Topics { + // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) + // } +} + +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 TestBlockFilterArgsNums(t *testing.T) { + input := `[{ + "fromBlock": 2, + "toBlock": 3 + }]` + + args := new(BlockFilterArgs) + err := json.Unmarshal([]byte(input), &args) + switch err.(type) { + case *DecodeParamError: + break + default: + t.Errorf("Should have *DecodeParamError but instead have %T", err) + } + +} + +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 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 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 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 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 TestWhsiperFilterArgs(t *testing.T) { + input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": "0x34ag445g3455b34"}]` + expected := new(WhisperFilterArgs) + expected.From = "" + expected.To = "0x34ag445g3455b34" + expected.Topics = []string{"0x68656c6c6f20776f726c64"} + + args := new(WhisperFilterArgs) + 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.Topics != args.Topics { + // t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + // } +} + +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 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 err := args.requirements(); 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) + err := json.Unmarshal([]byte(input), &args) + if err == nil { + t.Error("Expected error but didn't get one") + } +} + +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 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 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 TestSubmitWorkArgs(t *testing.T) { + input := `["0x0000000000000001", "0x1234567890abcdef1234567890abcdef", "0xD1GE5700000000000000000000000000"]` + expected := new(SubmitWorkArgs) + expected.Nonce = 1 + expected.Header = common.HexToHash("0x1234567890abcdef1234567890abcdef") + expected.Digest = common.HexToHash("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) + } +} diff --git a/rpc/http.go b/rpc/http.go new file mode 100644 index 000000000..3dfb67781 --- /dev/null +++ b/rpc/http.go @@ -0,0 +1,101 @@ +package rpc + +import ( + "encoding/json" + "io" + "io/ioutil" + "net/http" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/xeth" +) + +var rpclogger = logger.NewLogger("RPC") + +const ( + jsonrpcver = "2.0" + maxSizeReqLength = 1024 * 1024 // 1MB +) + +// JSONRPC returns a handler that implements the Ethereum JSON-RPC API. +func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler { + api := NewEthereumApi(pipe, dataDir) + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + // TODO this needs to be configurable + w.Header().Set("Access-Control-Allow-Origin", "*") + + // 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: + 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..4e83be8a6 --- /dev/null +++ b/rpc/jeth.go @@ -0,0 +1,54 @@ +package rpc + +import ( + "encoding/json" + // "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 { + return self.err(-32603, err.Error(), req.Id) + } + self.re.Set("ret_jsonrpc", jsonrpcver) + self.re.Set("ret_id", req.Id) + self.re.Set("ret_result", respif) + response, err = self.re.Run(` + ret_response = { jsonrpc: ret_jsonrpc, id: ret_id, result: ret_result }; + `) + return +} diff --git a/rpc/messages.go b/rpc/messages.go new file mode 100644 index 000000000..7f5ebab11 --- /dev/null +++ b/rpc/messages.go @@ -0,0 +1,108 @@ +/* + 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/json" + "fmt" +) + +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/messages_test.go b/rpc/messages_test.go new file mode 100644 index 000000000..5274c91e4 --- /dev/null +++ b/rpc/messages_test.go @@ -0,0 +1,41 @@ +package rpc + +import ( + "testing" +) + +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()) + } +} diff --git a/rpc/responses.go b/rpc/responses.go new file mode 100644 index 000000000..993f467ea --- /dev/null +++ b/rpc/responses.go @@ -0,0 +1,243 @@ +package rpc + +import ( + "encoding/json" + // "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/state" +) + +type BlockRes struct { + fullTx bool + + BlockNumber int64 `json:"number"` + BlockHash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` + Nonce [8]byte `json:"nonce"` + Sha3Uncles common.Hash `json:"sha3Uncles"` + LogsBloom types.Bloom `json:"logsBloom"` + TransactionRoot common.Hash `json:"transactionRoot"` + StateRoot common.Hash `json:"stateRoot"` + Miner common.Address `json:"miner"` + Difficulty int64 `json:"difficulty"` + TotalDifficulty int64 `json:"totalDifficulty"` + Size int64 `json:"size"` + ExtraData []byte `json:"extraData"` + GasLimit int64 `json:"gasLimit"` + MinGasPrice int64 `json:"minGasPrice"` + GasUsed int64 `json:"gasUsed"` + UnixTimestamp int64 `json:"timestamp"` + Transactions []*TransactionRes `json:"transactions"` + Uncles []common.Hash `json:"uncles"` +} + +func (b *BlockRes) MarshalJSON() ([]byte, error) { + var ext struct { + BlockNumber string `json:"number"` + BlockHash string `json:"hash"` + ParentHash string `json:"parentHash"` + Nonce string `json:"nonce"` + Sha3Uncles string `json:"sha3Uncles"` + LogsBloom string `json:"logsBloom"` + TransactionRoot string `json:"transactionRoot"` + StateRoot string `json:"stateRoot"` + Miner string `json:"miner"` + Difficulty string `json:"difficulty"` + TotalDifficulty string `json:"totalDifficulty"` + Size string `json:"size"` + ExtraData string `json:"extraData"` + GasLimit string `json:"gasLimit"` + MinGasPrice string `json:"minGasPrice"` + GasUsed string `json:"gasUsed"` + UnixTimestamp string `json:"timestamp"` + Transactions []interface{} `json:"transactions"` + Uncles []string `json:"uncles"` + } + + // convert strict types to hexified strings + ext.BlockNumber = common.ToHex(big.NewInt(b.BlockNumber).Bytes()) + ext.BlockHash = b.BlockHash.Hex() + ext.ParentHash = b.ParentHash.Hex() + ext.Nonce = common.ToHex(b.Nonce[:]) + ext.Sha3Uncles = b.Sha3Uncles.Hex() + ext.LogsBloom = common.ToHex(b.LogsBloom[:]) + ext.TransactionRoot = b.TransactionRoot.Hex() + ext.StateRoot = b.StateRoot.Hex() + ext.Miner = b.Miner.Hex() + ext.Difficulty = common.ToHex(big.NewInt(b.Difficulty).Bytes()) + ext.TotalDifficulty = common.ToHex(big.NewInt(b.TotalDifficulty).Bytes()) + ext.Size = common.ToHex(big.NewInt(b.Size).Bytes()) + // ext.ExtraData = common.ToHex(b.ExtraData) + ext.GasLimit = common.ToHex(big.NewInt(b.GasLimit).Bytes()) + // ext.MinGasPrice = common.ToHex(big.NewInt(b.MinGasPrice).Bytes()) + ext.GasUsed = common.ToHex(big.NewInt(b.GasUsed).Bytes()) + ext.UnixTimestamp = common.ToHex(big.NewInt(b.UnixTimestamp).Bytes()) + ext.Transactions = make([]interface{}, len(b.Transactions)) + if b.fullTx { + for i, tx := range b.Transactions { + ext.Transactions[i] = tx + } + } else { + for i, tx := range b.Transactions { + ext.Transactions[i] = tx.Hash.Hex() + } + } + ext.Uncles = make([]string, len(b.Uncles)) + for i, v := range b.Uncles { + ext.Uncles[i] = v.Hex() + } + + return json.Marshal(ext) +} + +func NewBlockRes(block *types.Block) *BlockRes { + if block == nil { + return &BlockRes{} + } + + res := new(BlockRes) + res.BlockNumber = block.Number().Int64() + res.BlockHash = block.Hash() + res.ParentHash = block.ParentHash() + res.Nonce = block.Header().Nonce + res.Sha3Uncles = block.Header().UncleHash + res.LogsBloom = block.Bloom() + res.TransactionRoot = block.Header().TxHash + res.StateRoot = block.Root() + res.Miner = block.Header().Coinbase + res.Difficulty = block.Difficulty().Int64() + if block.Td != nil { + res.TotalDifficulty = block.Td.Int64() + } + res.Size = int64(block.Size()) + // res.ExtraData = + res.GasLimit = block.GasLimit().Int64() + // res.MinGasPrice = + res.GasUsed = block.GasUsed().Int64() + res.UnixTimestamp = block.Time() + res.Transactions = make([]*TransactionRes, len(block.Transactions())) + for i, tx := range block.Transactions() { + v := NewTransactionRes(tx) + v.BlockHash = block.Hash() + v.BlockNumber = block.Number().Int64() + v.TxIndex = int64(i) + res.Transactions[i] = v + } + res.Uncles = make([]common.Hash, len(block.Uncles())) + for i, uncle := range block.Uncles() { + res.Uncles[i] = uncle.Hash() + } + return res +} + +type TransactionRes struct { + Hash common.Hash `json:"hash"` + Nonce int64 `json:"nonce"` + BlockHash common.Hash `json:"blockHash,omitempty"` + BlockNumber int64 `json:"blockNumber,omitempty"` + TxIndex int64 `json:"transactionIndex,omitempty"` + From common.Address `json:"from"` + To *common.Address `json:"to"` + Value int64 `json:"value"` + Gas int64 `json:"gas"` + GasPrice int64 `json:"gasPrice"` + Input []byte `json:"input"` +} + +func (t *TransactionRes) MarshalJSON() ([]byte, error) { + var ext struct { + Hash string `json:"hash"` + Nonce string `json:"nonce"` + BlockHash string `json:"blockHash,omitempty"` + BlockNumber string `json:"blockNumber,omitempty"` + TxIndex string `json:"transactionIndex,omitempty"` + From string `json:"from"` + To string `json:"to"` + Value string `json:"value"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Input string `json:"input"` + } + + ext.Hash = t.Hash.Hex() + ext.Nonce = common.ToHex(big.NewInt(t.Nonce).Bytes()) + ext.BlockHash = t.BlockHash.Hex() + ext.BlockNumber = common.ToHex(big.NewInt(t.BlockNumber).Bytes()) + ext.TxIndex = common.ToHex(big.NewInt(t.TxIndex).Bytes()) + ext.From = t.From.Hex() + if t.To == nil { + ext.To = "0x00" + } else { + ext.To = t.To.Hex() + } + ext.Value = common.ToHex(big.NewInt(t.Value).Bytes()) + ext.Gas = common.ToHex(big.NewInt(t.Gas).Bytes()) + ext.GasPrice = common.ToHex(big.NewInt(t.GasPrice).Bytes()) + ext.Input = common.ToHex(t.Input) + + return json.Marshal(ext) +} + +func NewTransactionRes(tx *types.Transaction) *TransactionRes { + var v = new(TransactionRes) + v.Hash = tx.Hash() + v.Nonce = int64(tx.Nonce()) + v.From, _ = tx.From() + v.To = tx.To() + v.Value = tx.Value().Int64() + v.Gas = tx.Gas().Int64() + v.GasPrice = tx.GasPrice().Int64() + v.Input = 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 string `json:"address"` + Topics []string `json:"topics"` + Data string `json:"data"` + Number uint64 `json:"number"` +} + +func NewLogsRes(logs state.Logs) (ls []LogRes) { + ls = make([]LogRes, len(logs)) + + for i, log := range logs { + var l LogRes + l.Topics = make([]string, len(log.Topics())) + l.Address = log.Address().Hex() + l.Data = common.ToHex(log.Data()) + l.Number = log.Number() + for j, topic := range log.Topics() { + l.Topics[j] = topic.Hex() + } + ls[i] = l + } + + return +} |