From 9383c743dd0ff250af41334ee07dcbe5dfb40546 Mon Sep 17 00:00:00 2001 From: Austin Abell Date: Tue, 24 Sep 2019 17:38:58 -0400 Subject: [PATCH] Implement block tags (#106) * Implemented tm/ethermint specific block tags * Fix edge case for block query --- rpc/eth_api.go | 30 ++++++++++++++---------- rpc/types.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 rpc/types.go diff --git a/rpc/eth_api.go b/rpc/eth_api.go index aca871d2..14484bad 100644 --- a/rpc/eth_api.go +++ b/rpc/eth_api.go @@ -17,7 +17,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" @@ -113,7 +112,7 @@ func (e *PublicEthAPI) BlockNumber() (hexutil.Uint64, error) { } // GetBalance returns the provided account's balance up to the provided block number. -func (e *PublicEthAPI) GetBalance(address common.Address, blockNum rpc.BlockNumber) (*hexutil.Big, error) { +func (e *PublicEthAPI) GetBalance(address common.Address, blockNum BlockNumber) (*hexutil.Big, error) { ctx := e.cliCtx.WithHeight(blockNum.Int64()) res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/balance/%s", types.ModuleName, address), nil) if err != nil { @@ -127,7 +126,7 @@ func (e *PublicEthAPI) GetBalance(address common.Address, blockNum rpc.BlockNumb } // GetStorageAt returns the contract storage at the given address, block number, and key. -func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum rpc.BlockNumber) (hexutil.Bytes, error) { +func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum BlockNumber) (hexutil.Bytes, error) { ctx := e.cliCtx.WithHeight(blockNum.Int64()) res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/storage/%s/%s", types.ModuleName, address, key), nil) if err != nil { @@ -140,7 +139,7 @@ func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum } // GetTransactionCount returns the number of transactions at the given address up to the given block number. -func (e *PublicEthAPI) GetTransactionCount(address common.Address, blockNum rpc.BlockNumber) (*hexutil.Uint64, error) { +func (e *PublicEthAPI) GetTransactionCount(address common.Address, blockNum BlockNumber) (*hexutil.Uint64, error) { ctx := e.cliCtx.WithHeight(blockNum.Int64()) res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/nonce/%s", types.ModuleName, address), nil) if err != nil { @@ -158,7 +157,7 @@ func (e *PublicEthAPI) GetBlockTransactionCountByHash(hash common.Hash) hexutil. } // GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number. -func (e *PublicEthAPI) GetBlockTransactionCountByNumber(blockNum rpc.BlockNumber) (hexutil.Uint, error) { +func (e *PublicEthAPI) GetBlockTransactionCountByNumber(blockNum BlockNumber) (hexutil.Uint, error) { node, err := e.cliCtx.GetNode() if err != nil { return 0, err @@ -179,12 +178,12 @@ func (e *PublicEthAPI) GetUncleCountByBlockHash(hash common.Hash) hexutil.Uint { } // GetUncleCountByBlockNumber returns the number of uncles in the block idenfied by number. Always zero. -func (e *PublicEthAPI) GetUncleCountByBlockNumber(blockNum rpc.BlockNumber) hexutil.Uint { +func (e *PublicEthAPI) GetUncleCountByBlockNumber(blockNum BlockNumber) hexutil.Uint { return 0 } // GetCode returns the contract code at the given address and block number. -func (e *PublicEthAPI) GetCode(address common.Address, blockNumber rpc.BlockNumber) (hexutil.Bytes, error) { +func (e *PublicEthAPI) GetCode(address common.Address, blockNumber BlockNumber) (hexutil.Bytes, error) { ctx := e.cliCtx.WithHeight(blockNumber.Int64()) res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/code/%s", types.ModuleName, address), nil) if err != nil { @@ -304,12 +303,12 @@ type CallArgs struct { } // Call performs a raw contract call. -func (e *PublicEthAPI) Call(args CallArgs, blockNum rpc.BlockNumber) hexutil.Bytes { +func (e *PublicEthAPI) Call(args CallArgs, blockNum BlockNumber) hexutil.Bytes { return nil } // EstimateGas estimates gas usage for the given smart contract call. -func (e *PublicEthAPI) EstimateGas(args CallArgs, blockNum rpc.BlockNumber) hexutil.Uint64 { +func (e *PublicEthAPI) EstimateGas(args CallArgs, blockNum BlockNumber) hexutil.Uint64 { return 0 } @@ -319,9 +318,16 @@ func (e *PublicEthAPI) GetBlockByHash(hash common.Hash, fullTx bool) map[string] } // GetBlockByNumber returns the block identified by number. -func (e *PublicEthAPI) GetBlockByNumber(blockNum rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { +func (e *PublicEthAPI) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error) { value := blockNum.Int64() - block, err := e.cliCtx.Client.Block(&value) + + // Remove this check when 0 query is fixed ref: (https://github.com/tendermint/tendermint/issues/4014) + var blkNumPtr *int64 + if value != 0 { + blkNumPtr = &value + } + + block, err := e.cliCtx.Client.Block(blkNumPtr) if err != nil { return nil, err } @@ -450,7 +456,7 @@ func (e *PublicEthAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx h } // GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index. -func (e *PublicEthAPI) GetTransactionByBlockNumberAndIndex(blockNumber rpc.BlockNumber, idx hexutil.Uint) *Transaction { +func (e *PublicEthAPI) GetTransactionByBlockNumberAndIndex(blockNumber BlockNumber, idx hexutil.Uint) *Transaction { return nil } diff --git a/rpc/types.go b/rpc/types.go new file mode 100644 index 00000000..89008f6f --- /dev/null +++ b/rpc/types.go @@ -0,0 +1,62 @@ +package rpc + +import ( + "fmt" + "math" + "strings" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// BlockNumber represents decoding hex string to block values +type BlockNumber int64 + +const ( + // LatestBlockNumber mapping from "latest" to 0 for tm query + LatestBlockNumber = BlockNumber(0) + + // EarliestBlockNumber mapping from "earliest" to 1 for tm query (earliest query not supported) + EarliestBlockNumber = BlockNumber(1) +) + +// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: +// - "latest", "earliest" or "pending" as string arguments +// - the block number +// Returned errors: +// - an invalid block number error when the given argument isn't a known strings +// - an out of range error when the given block number is either too little or too large +func (bn *BlockNumber) UnmarshalJSON(data []byte) error { + input := strings.TrimSpace(string(data)) + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] + } + + switch input { + case "earliest": + *bn = EarliestBlockNumber + return nil + case "latest": + *bn = LatestBlockNumber + return nil + case "pending": + return fmt.Errorf("pending queries not implemented") + // *bn = PendingBlockNumber + // return nil + } + + blckNum, err := hexutil.DecodeUint64(input) + if err != nil { + return err + } + if blckNum > math.MaxInt64 { + return fmt.Errorf("Blocknumber too high") + } + + *bn = BlockNumber(blckNum) + return nil +} + +// Int64 converts block number to primitive type +func (bn BlockNumber) Int64() int64 { + return (int64)(bn) +}