diff --git a/CHANGELOG.md b/CHANGELOG.md index df407499a6..403ce5e90e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (cli) [#1230](https://github.com/evmos/ethermint/pull/1230) Remove redundant positional height parameter from feemarket's query cli. * (ante) [#1289](https://github.com/evmos/ethermint/pull/1289) Change the fallback tx priority mechanism to be based on gas price. * (test) [#1311](https://github.com/evmos/ethermint/pull/1311) add integration test for the rollback cmd +* (rpc) [#1296](https://github.com/evmos/ethermint/pull/1296) add backend blocks.go unit tests. ### Bug Fixes diff --git a/rpc/backend/account_info.go b/rpc/backend/account_info.go index 69fed508bc..9067d5e689 100644 --- a/rpc/backend/account_info.go +++ b/rpc/backend/account_info.go @@ -17,7 +17,7 @@ import ( // GetCode returns the contract code at the given address and block number. func (b *Backend) GetCode(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) { - blockNum, err := b.GetBlockNumber(blockNrOrHash) + blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash) if err != nil { return nil, err } @@ -35,17 +35,14 @@ func (b *Backend) GetCode(address common.Address, blockNrOrHash rpctypes.BlockNu } // GetProof returns an account object with proof and any storage proofs -func (b *Backend) GetProof(address common.Address, - storageKeys []string, - blockNrOrHash rpctypes.BlockNumberOrHash, -) (*rpctypes.AccountResult, error) { - blockNum, err := b.GetBlockNumber(blockNrOrHash) +func (b *Backend) GetProof(address common.Address, storageKeys []string, blockNrOrHash rpctypes.BlockNumberOrHash) (*rpctypes.AccountResult, error) { + blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash) if err != nil { return nil, err } height := blockNum.Int64() - _, err = b.GetTendermintBlockByNumber(blockNum) + _, err = b.TendermintBlockByNumber(blockNum) if err != nil { // the error message imitates geth behavior return nil, errors.New("header not found") @@ -132,7 +129,7 @@ func (b *Backend) GetProof(address common.Address, // GetStorageAt returns the contract storage at the given address, block number, and key. func (b *Backend) GetStorageAt(address common.Address, key string, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) { - blockNum, err := b.GetBlockNumber(blockNrOrHash) + blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash) if err != nil { return nil, err } @@ -153,7 +150,7 @@ func (b *Backend) GetStorageAt(address common.Address, key string, blockNrOrHash // GetBalance returns the provided account's balance up to the provided block number. func (b *Backend) GetBalance(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Big, error) { - blockNum, err := b.GetBlockNumber(blockNrOrHash) + blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash) if err != nil { return nil, err } @@ -162,7 +159,7 @@ func (b *Backend) GetBalance(address common.Address, blockNrOrHash rpctypes.Bloc Address: address.String(), } - _, err = b.GetTendermintBlockByNumber(blockNum) + _, err = b.TendermintBlockByNumber(blockNum) if err != nil { return nil, err } diff --git a/rpc/backend/backend.go b/rpc/backend/backend.go index c8b4efdc93..edc891ad34 100644 --- a/rpc/backend/backend.go +++ b/rpc/backend/backend.go @@ -67,22 +67,21 @@ type EVMBackend interface { // Blocks Info BlockNumber() (hexutil.Uint64, error) GetBlockByNumber(blockNum rpctypes.BlockNumber, fullTx bool) (map[string]interface{}, error) - GetTendermintBlockByNumber(blockNum rpctypes.BlockNumber) (*tmrpctypes.ResultBlock, error) - GetTendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error) - GetTendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.ResultBlock, error) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) - BlockByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Block, error) - BlockByHash(blockHash common.Hash) (*ethtypes.Block, error) - GetBlockNumberByHash(blockHash common.Hash) (*big.Int, error) - GetBlockNumber(blockNrOrHash rpctypes.BlockNumberOrHash) (rpctypes.BlockNumber, error) GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumber) *hexutil.Uint + TendermintBlockByNumber(blockNum rpctypes.BlockNumber) (*tmrpctypes.ResultBlock, error) + TendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error) + TendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.ResultBlock, error) + BlockNumberFromTendermint(blockNrOrHash rpctypes.BlockNumberOrHash) (rpctypes.BlockNumber, error) + BlockNumberFromTendermintByHash(blockHash common.Hash) (*big.Int, error) + EthMsgsFromTendermintBlock(block *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults) []*evmtypes.MsgEthereumTx BlockBloom(blockRes *tmrpctypes.ResultBlockResults) (ethtypes.Bloom, error) - GetEthereumMsgsFromTendermintBlock(block *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults) []*evmtypes.MsgEthereumTx HeaderByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Header, error) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) - EthBlockFromTendermint(resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults, fullTx bool) (map[string]interface{}, error) - EthBlockFromTm(resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults) (*ethtypes.Block, error) + RPCBlockFromTendermintBlock(resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults, fullTx bool) (map[string]interface{}, error) + EthBlockByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Block, error) + EthBlockFromTendermintBlock(resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults) (*ethtypes.Block, error) // Account Info GetCode(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) @@ -117,6 +116,7 @@ type EVMBackend interface { SetTxDefaults(args evmtypes.TransactionArgs) (evmtypes.TransactionArgs, error) EstimateGas(args evmtypes.TransactionArgs, blockNrOptional *rpctypes.BlockNumber) (hexutil.Uint64, error) DoCall(args evmtypes.TransactionArgs, blockNr rpctypes.BlockNumber) (*evmtypes.MsgEthereumTxResponse, error) + GasPrice() (*hexutil.Big, error) // Filter API GetLogs(hash common.Hash) ([][]*ethtypes.Log, error) diff --git a/rpc/backend/blocks_info.go b/rpc/backend/blocks.go similarity index 70% rename from rpc/backend/blocks_info.go rename to rpc/backend/blocks.go index 653725a96d..b2cd8bd1a0 100644 --- a/rpc/backend/blocks_info.go +++ b/rpc/backend/blocks.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/trie" rpctypes "github.com/evmos/ethermint/rpc/types" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/pkg/errors" @@ -19,16 +20,10 @@ import ( "google.golang.org/grpc/metadata" ) -// Getting Blocks -// -// Retrieves information from a particular block in the blockchain. -// BlockNumber() (hexutil.Uint64, error) -// GetBlockByNumber(ethBlockNum rpctypes.BlockNumber, fullTx bool) (map[string]interface{}, error) -// GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) - -// BlockNumber returns the current block number in abci app state. -// Because abci app state could lag behind from tendermint latest block, it's more stable -// for the client to use the latest block number in abci app state than tendermint rpc. +// BlockNumber returns the current block number in abci app state. Because abci +// app state could lag behind from tendermint latest block, it's more stable for +// the client to use the latest block number in abci app state than tendermint +// rpc. func (b *Backend) BlockNumber() (hexutil.Uint64, error) { // do any grpc query, ignore the response and use the returned block height var header metadata.MD @@ -50,9 +45,11 @@ func (b *Backend) BlockNumber() (hexutil.Uint64, error) { return hexutil.Uint64(height), nil } -// GetBlockByNumber returns the block identified by number. +// GetBlockByNumber returns the JSON-RPC compatible Ethereum block identified by +// block number. Depending on fullTx it either returns the full transaction +// objects or if false only the hashes of the transactions. func (b *Backend) GetBlockByNumber(blockNum rpctypes.BlockNumber, fullTx bool) (map[string]interface{}, error) { - resBlock, err := b.GetTendermintBlockByNumber(blockNum) + resBlock, err := b.TendermintBlockByNumber(blockNum) if err != nil { return nil, nil } @@ -62,24 +59,25 @@ func (b *Backend) GetBlockByNumber(blockNum rpctypes.BlockNumber, fullTx bool) ( return nil, nil } - blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { b.logger.Debug("failed to fetch block result from Tendermint", "height", blockNum, "error", err.Error()) return nil, nil } - res, err := b.EthBlockFromTendermint(resBlock, blockRes, fullTx) + res, err := b.RPCBlockFromTendermintBlock(resBlock, blockRes, fullTx) if err != nil { - b.logger.Debug("EthBlockFromTendermint failed", "height", blockNum, "error", err.Error()) + b.logger.Debug("GetEthBlockFromTendermint failed", "height", blockNum, "error", err.Error()) return nil, err } return res, nil } -// GetBlockByHash returns the block identified by hash. +// GetBlockByHash returns the JSON-RPC compatible Ethereum block identified by +// hash. func (b *Backend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) { - resBlock, err := b.GetTendermintBlockByHash(hash) + resBlock, err := b.TendermintBlockByHash(hash) if err != nil { return nil, err } @@ -89,24 +87,71 @@ func (b *Backend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]inte return nil, nil } - blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { b.logger.Debug("failed to fetch block result from Tendermint", "block-hash", hash.String(), "error", err.Error()) return nil, nil } - res, err := b.EthBlockFromTendermint(resBlock, blockRes, fullTx) + res, err := b.RPCBlockFromTendermintBlock(resBlock, blockRes, fullTx) if err != nil { - b.logger.Debug("EthBlockFromTendermint failed", "hash", hash, "error", err.Error()) + b.logger.Debug("GetEthBlockFromTendermint failed", "hash", hash, "error", err.Error()) return nil, err } return res, nil } -// GetTendermintBlockByNumber returns a Tendermint formatted block for a given +// GetBlockTransactionCountByHash returns the number of Ethereum transactions in +// the block identified by hash. +func (b *Backend) GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint { + block, err := b.clientCtx.Client.BlockByHash(b.ctx, hash.Bytes()) + if err != nil { + b.logger.Debug("block not found", "hash", hash.Hex(), "error", err.Error()) + return nil + } + + if block.Block == nil { + b.logger.Debug("block not found", "hash", hash.Hex()) + return nil + } + + return b.GetBlockTransactionCount(block) +} + +// GetBlockTransactionCountByNumber returns the number of Ethereum transactions +// in the block identified by number. +func (b *Backend) GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumber) *hexutil.Uint { + block, err := b.TendermintBlockByNumber(blockNum) + if err != nil { + b.logger.Debug("block not found", "height", blockNum.Int64(), "error", err.Error()) + return nil + } + + if block.Block == nil { + b.logger.Debug("block not found", "height", blockNum.Int64()) + return nil + } + + return b.GetBlockTransactionCount(block) +} + +// GetBlockTransactionCount returns the number of Ethereum transactions in a +// given block. +func (b *Backend) GetBlockTransactionCount(block *tmrpctypes.ResultBlock) *hexutil.Uint { + blockRes, err := b.TendermintBlockResultByNumber(&block.Block.Height) + if err != nil { + return nil + } + + ethMsgs := b.EthMsgsFromTendermintBlock(block, blockRes) + n := hexutil.Uint(len(ethMsgs)) + return &n +} + +// TendermintBlockByNumber returns a Tendermint-formatted block for a given // block number -func (b *Backend) GetTendermintBlockByNumber(blockNum rpctypes.BlockNumber) (*tmrpctypes.ResultBlock, error) { +func (b *Backend) TendermintBlockByNumber(blockNum rpctypes.BlockNumber) (*tmrpctypes.ResultBlock, error) { height := blockNum.Int64() if height <= 0 { // fetch the latest block number from the app state, more accurate than the tendermint block store state. @@ -123,36 +168,21 @@ func (b *Backend) GetTendermintBlockByNumber(blockNum rpctypes.BlockNumber) (*tm } if resBlock.Block == nil { - b.logger.Debug("GetTendermintBlockByNumber block not found", "height", height) + b.logger.Debug("TendermintBlockByNumber block not found", "height", height) return nil, nil } return resBlock, nil } -// BlockBloom query block bloom filter from block results -func (b *Backend) BlockBloom(blockRes *tmrpctypes.ResultBlockResults) (ethtypes.Bloom, error) { - for _, event := range blockRes.EndBlockEvents { - if event.Type != evmtypes.EventTypeBlockBloom { - continue - } - - for _, attr := range event.Attributes { - if bytes.Equal(attr.Key, bAttributeKeyEthereumBloom) { - return ethtypes.BytesToBloom(attr.Value), nil - } - } - } - return ethtypes.Bloom{}, errors.New("block bloom event is not found") -} - -// GetTendermintBlockResultByNumber returns a Tendermint-formatted block result by block number -func (b *Backend) GetTendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error) { +// TendermintBlockResultByNumber returns a Tendermint-formatted block result +// by block number +func (b *Backend) TendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error) { return b.clientCtx.Client.BlockResults(b.ctx, height) } -// GetTendermintBlockByHash returns a Tendermint format block by block number -func (b *Backend) GetTendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.ResultBlock, error) { +// TendermintBlockByHash returns a Tendermint-formatted block by block number +func (b *Backend) TendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.ResultBlock, error) { resBlock, err := b.clientCtx.Client.BlockByHash(b.ctx, blockHash.Bytes()) if err != nil { b.logger.Debug("tendermint client failed to get block", "blockHash", blockHash.Hex(), "error", err.Error()) @@ -160,70 +190,20 @@ func (b *Backend) GetTendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.R } if resBlock == nil || resBlock.Block == nil { - b.logger.Debug("GetTendermintBlockByHash block not found", "blockHash", blockHash.Hex()) + b.logger.Debug("TendermintBlockByHash block not found", "blockHash", blockHash.Hex()) return nil, nil } return resBlock, nil } -// BlockByNumber returns the block identified by number. -func (b *Backend) BlockByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Block, error) { - resBlock, err := b.GetTendermintBlockByNumber(blockNum) - if err != nil { - return nil, err - } - if resBlock == nil { - // block not found - return nil, fmt.Errorf("block not found for height %d", blockNum) - } - - blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) - if err != nil { - return nil, fmt.Errorf("block result not found for height %d", resBlock.Block.Height) - } - - return b.EthBlockFromTm(resBlock, blockRes) -} - -// BlockByHash returns the block identified by hash. -func (b *Backend) BlockByHash(hash common.Hash) (*ethtypes.Block, error) { - resBlock, err := b.GetTendermintBlockByHash(hash) - if err != nil { - return nil, err - } - - if resBlock == nil || resBlock.Block == nil { - return nil, fmt.Errorf("block not found for hash %s", hash) - } - - blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) - if err != nil { - return nil, fmt.Errorf("block result not found for hash %s", hash) - } - - return b.EthBlockFromTm(resBlock, blockRes) -} - -// GetBlockNumberByHash returns the block height of given block hash -func (b *Backend) GetBlockNumberByHash(blockHash common.Hash) (*big.Int, error) { - resBlock, err := b.GetTendermintBlockByHash(blockHash) - if err != nil { - return nil, err - } - if resBlock == nil { - return nil, errors.Errorf("block not found for hash %s", blockHash.Hex()) - } - return big.NewInt(resBlock.Block.Height), nil -} - -// getBlockNumber returns the BlockNumber from BlockNumberOrHash -func (b *Backend) GetBlockNumber(blockNrOrHash rpctypes.BlockNumberOrHash) (rpctypes.BlockNumber, error) { +// BlockNumberFromTendermint returns the BlockNumber from BlockNumberOrHash +func (b *Backend) BlockNumberFromTendermint(blockNrOrHash rpctypes.BlockNumberOrHash) (rpctypes.BlockNumber, error) { switch { case blockNrOrHash.BlockHash == nil && blockNrOrHash.BlockNumber == nil: return rpctypes.EthEarliestBlockNumber, fmt.Errorf("types BlockHash and BlockNumber cannot be both nil") case blockNrOrHash.BlockHash != nil: - blockNumber, err := b.GetBlockNumberByHash(*blockNrOrHash.BlockHash) + blockNumber, err := b.BlockNumberFromTendermintByHash(*blockNrOrHash.BlockHash) if err != nil { return rpctypes.EthEarliestBlockNumber, err } @@ -235,56 +215,22 @@ func (b *Backend) GetBlockNumber(blockNrOrHash rpctypes.BlockNumberOrHash) (rpct } } -// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash. -func (b *Backend) GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint { - block, err := b.clientCtx.Client.BlockByHash(b.ctx, hash.Bytes()) +// BlockNumberFromTendermintByHash returns the block height of given block hash +func (b *Backend) BlockNumberFromTendermintByHash(blockHash common.Hash) (*big.Int, error) { + resBlock, err := b.TendermintBlockByHash(blockHash) if err != nil { - b.logger.Debug("block not found", "hash", hash.Hex(), "error", err.Error()) - return nil - } - - if block.Block == nil { - b.logger.Debug("block not found", "hash", hash.Hex()) - return nil - } - - blockRes, err := b.GetTendermintBlockResultByNumber(&block.Block.Height) - if err != nil { - return nil - } - - ethMsgs := b.GetEthereumMsgsFromTendermintBlock(block, blockRes) - n := hexutil.Uint(len(ethMsgs)) - return &n -} - -// GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number. -func (b *Backend) GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumber) *hexutil.Uint { - block, err := b.GetTendermintBlockByNumber(blockNum) - if err != nil { - b.logger.Debug("block not found", "height", blockNum.Int64(), "error", err.Error()) - return nil - } - - if block.Block == nil { - b.logger.Debug("block not found", "height", blockNum.Int64()) - return nil + return nil, err } - - blockRes, err := b.GetTendermintBlockResultByNumber(&block.Block.Height) - if err != nil { - return nil + if resBlock == nil { + return nil, errors.Errorf("block not found for hash %s", blockHash.Hex()) } - - ethMsgs := b.GetEthereumMsgsFromTendermintBlock(block, blockRes) - n := hexutil.Uint(len(ethMsgs)) - return &n + return big.NewInt(resBlock.Block.Height), nil } -// GetEthereumMsgsFromTendermintBlock returns all real MsgEthereumTxs from a +// EthMsgsFromTendermintBlock returns all real MsgEthereumTxs from a // Tendermint block. It also ensures consistency over the correct txs indexes // across RPC endpoints -func (b *Backend) GetEthereumMsgsFromTendermintBlock( +func (b *Backend) EthMsgsFromTendermintBlock( resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults, ) []*evmtypes.MsgEthereumTx { @@ -324,7 +270,7 @@ func (b *Backend) GetEthereumMsgsFromTendermintBlock( // HeaderByNumber returns the block header identified by height. func (b *Backend) HeaderByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Header, error) { - resBlock, err := b.GetTendermintBlockByNumber(blockNum) + resBlock, err := b.TendermintBlockByNumber(blockNum) if err != nil { return nil, err } @@ -333,7 +279,7 @@ func (b *Backend) HeaderByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Heade return nil, errors.Errorf("block not found for height %d", blockNum) } - blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { return nil, fmt.Errorf("block result not found for height %d", resBlock.Block.Height) } @@ -355,7 +301,7 @@ func (b *Backend) HeaderByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Heade // HeaderByHash returns the block header identified by hash. func (b *Backend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) { - resBlock, err := b.GetTendermintBlockByHash(blockHash) + resBlock, err := b.TendermintBlockByHash(blockHash) if err != nil { return nil, err } @@ -363,7 +309,7 @@ func (b *Backend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) return nil, errors.Errorf("block not found for hash %s", blockHash.Hex()) } - blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { return nil, errors.Errorf("block result not found for height %d", resBlock.Block.Height) } @@ -383,9 +329,25 @@ func (b *Backend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) return ethHeader, nil } -// EthBlockFromTendermint returns a JSON-RPC compatible Ethereum block from a +// BlockBloom query block bloom filter from block results +func (b *Backend) BlockBloom(blockRes *tmrpctypes.ResultBlockResults) (ethtypes.Bloom, error) { + for _, event := range blockRes.EndBlockEvents { + if event.Type != evmtypes.EventTypeBlockBloom { + continue + } + + for _, attr := range event.Attributes { + if bytes.Equal(attr.Key, bAttributeKeyEthereumBloom) { + return ethtypes.BytesToBloom(attr.Value), nil + } + } + } + return ethtypes.Bloom{}, errors.New("block bloom event is not found") +} + +// RPCBlockFromTendermintBlock returns a JSON-RPC compatible Ethereum block from a // given Tendermint block and its block result. -func (b *Backend) EthBlockFromTendermint( +func (b *Backend) RPCBlockFromTendermintBlock( resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults, fullTx bool, @@ -399,7 +361,7 @@ func (b *Backend) EthBlockFromTendermint( b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", block.Height, "error", err) } - msgs := b.GetEthereumMsgsFromTendermintBlock(resBlock, blockRes) + msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) for txIndex, ethMsg := range msgs { if !fullTx { hash := common.HexToHash(ethMsg.Hash) @@ -477,8 +439,31 @@ func (b *Backend) EthBlockFromTendermint( return formattedBlock, nil } -// Returns and Ethereum Block type from Tendermint block -func (b *Backend) EthBlockFromTm(resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults) (*ethtypes.Block, error) { +// EthBlockByNumber returns the Ethereum Block identified by number. +func (b *Backend) EthBlockByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Block, error) { + resBlock, err := b.TendermintBlockByNumber(blockNum) + if err != nil { + return nil, err + } + if resBlock == nil { + // block not found + return nil, fmt.Errorf("block not found for height %d", blockNum) + } + + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + return nil, fmt.Errorf("block result not found for height %d", resBlock.Block.Height) + } + + return b.EthBlockFromTendermintBlock(resBlock, blockRes) +} + +// EthBlockFromTendermintBlock returns an Ethereum Block type from Tendermint block +// EthBlockFromTendermintBlock +func (b *Backend) EthBlockFromTendermintBlock( + resBlock *tmrpctypes.ResultBlock, + blockRes *tmrpctypes.ResultBlockResults, +) (*ethtypes.Block, error) { block := resBlock.Block height := block.Height bloom, err := b.BlockBloom(blockRes) @@ -493,13 +478,7 @@ func (b *Backend) EthBlockFromTm(resBlock *tmrpctypes.ResultBlock, blockRes *tmr } ethHeader := rpctypes.EthHeaderFromTendermint(block.Header, bloom, baseFee) - - resBlockResult, err := b.GetTendermintBlockResultByNumber(&block.Height) - if err != nil { - return nil, err - } - - msgs := b.GetEthereumMsgsFromTendermintBlock(resBlock, resBlockResult) + msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) txs := make([]*ethtypes.Transaction, len(msgs)) for i, ethMsg := range msgs { @@ -507,6 +486,6 @@ func (b *Backend) EthBlockFromTm(resBlock *tmrpctypes.ResultBlock, blockRes *tmr } // TODO: add tx receipts - ethBlock := ethtypes.NewBlock(ethHeader, txs, nil, nil, nil) + ethBlock := ethtypes.NewBlock(ethHeader, txs, nil, nil, trie.NewStackTrie(nil)) return ethBlock, nil } diff --git a/rpc/backend/evm_backend_test.go b/rpc/backend/blocks_test.go similarity index 54% rename from rpc/backend/evm_backend_test.go rename to rpc/backend/blocks_test.go index 4e78917b09..e4eff35eb3 100644 --- a/rpc/backend/evm_backend_test.go +++ b/rpc/backend/blocks_test.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/trie" "github.com/tendermint/tendermint/abci/types" tmrpctypes "github.com/tendermint/tendermint/rpc/core/types" tmtypes "github.com/tendermint/tendermint/types" @@ -17,7 +18,6 @@ import ( ethrpc "github.com/evmos/ethermint/rpc/types" "github.com/evmos/ethermint/tests" evmtypes "github.com/evmos/ethermint/x/evm/types" - feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" ) func (suite *BackendTestSuite) TestBlockNumber() { @@ -242,6 +242,54 @@ func (suite *BackendTestSuite) TestGetBlockByHash() { expNoop bool expPass bool }{ + { + "fail - tendermint failed to get block", + common.BytesToHash(block.Hash()), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(hash common.Hash, baseFee sdk.Int, validator sdk.AccAddress, txBz []byte) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, txBz) + }, + false, + false, + }, + { + "noop - tendermint blockres not found", + common.BytesToHash(block.Hash()), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(hash common.Hash, baseFee sdk.Int, validator sdk.AccAddress, txBz []byte) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashNotFound(client, hash, txBz) + }, + true, + true, + }, + { + "noop - tendermint failed to fetch block result", + common.BytesToHash(block.Hash()), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(hash common.Hash, baseFee sdk.Int, validator sdk.AccAddress, txBz []byte) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, hash, txBz) + + RegisterBlockResultsError(client, height) + }, + true, + true, + }, { "pass - without tx", common.BytesToHash(block.Hash()), @@ -319,7 +367,156 @@ func (suite *BackendTestSuite) TestGetBlockByHash() { } } -func (suite *BackendTestSuite) TestGetTendermintBlockByNumber() { +func (suite *BackendTestSuite) TestGetBlockTransactionCountByHash() { + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + hash common.Hash + registerMock func(common.Hash) + expCount hexutil.Uint + expPass bool + }{ + { + "fail - block not found", + common.BytesToHash(emptyBlock.Hash()), + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, nil) + }, + hexutil.Uint(0), + false, + }, + { + "fail - tendermint client failed to get block result", + common.BytesToHash(emptyBlock.Hash()), + func(hash common.Hash) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, nil) + RegisterBlockResultsError(client, height) + }, + hexutil.Uint(0), + false, + }, + { + "pass - block without tx", + common.BytesToHash(emptyBlock.Hash()), + func(hash common.Hash) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, nil) + RegisterBlockResults(client, height) + }, + hexutil.Uint(0), + true, + }, + { + "pass - block with tx", + common.BytesToHash(block.Hash()), + func(hash common.Hash) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, bz) + RegisterBlockResults(client, height) + }, + hexutil.Uint(1), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.hash) + count := suite.backend.GetBlockTransactionCountByHash(tc.hash) + if tc.expPass { + suite.Require().Equal(tc.expCount, *count) + } else { + suite.Require().Nil(count) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetBlockTransactionCountByNumber() { + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + blockNum ethrpc.BlockNumber + registerMock func(ethrpc.BlockNumber) + expCount hexutil.Uint + expPass bool + }{ + { + "fail - block not found", + ethrpc.BlockNumber(emptyBlock.Height), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, height) + }, + hexutil.Uint(0), + false, + }, + { + "fail - tendermint client failed to get block result", + ethrpc.BlockNumber(emptyBlock.Height), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + RegisterBlockResultsError(client, height) + }, + hexutil.Uint(0), + false, + }, + { + "pass - block without tx", + ethrpc.BlockNumber(emptyBlock.Height), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + RegisterBlockResults(client, height) + }, + hexutil.Uint(0), + true, + }, + { + "pass - block with tx", + ethrpc.BlockNumber(block.Height), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, bz) + RegisterBlockResults(client, height) + }, + hexutil.Uint(1), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.blockNum) + count := suite.backend.GetBlockTransactionCountByNumber(tc.blockNum) + if tc.expPass { + suite.Require().Equal(tc.expCount, *count) + } else { + suite.Require().Nil(count) + } + }) + } +} + +func (suite *BackendTestSuite) TestTendermintBlockByNumber() { var expResultBlock *tmrpctypes.ResultBlock testCases := []struct { @@ -407,7 +604,7 @@ func (suite *BackendTestSuite) TestGetTendermintBlockByNumber() { suite.SetupTest() // reset test and queries tc.registerMock(tc.blockNumber) - resultBlock, err := suite.backend.GetTendermintBlockByNumber(tc.blockNumber) + resultBlock, err := suite.backend.TendermintBlockByNumber(tc.blockNumber) if tc.expPass { suite.Require().NoError(err) @@ -425,7 +622,7 @@ func (suite *BackendTestSuite) TestGetTendermintBlockByNumber() { } } -func (suite *BackendTestSuite) TestGetTendermintBlockResultByNumber() { +func (suite *BackendTestSuite) TestTendermintBlockResultByNumber() { var expBlockRes *tmrpctypes.ResultBlockResults testCases := []struct { @@ -463,7 +660,7 @@ func (suite *BackendTestSuite) TestGetTendermintBlockResultByNumber() { suite.SetupTest() // reset test and queries tc.registerMock(tc.blockNumber) - blockRes, err := suite.backend.GetTendermintBlockResultByNumber(&tc.blockNumber) + blockRes, err := suite.backend.TendermintBlockResultByNumber(&tc.blockNumber) if tc.expPass { suite.Require().NoError(err) @@ -475,6 +672,141 @@ func (suite *BackendTestSuite) TestGetTendermintBlockResultByNumber() { } } +func (suite *BackendTestSuite) TestBlockNumberFromTendermint() { + var resBlock *tmrpctypes.ResultBlock + + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + blockNum := ethrpc.NewBlockNumber(big.NewInt(block.Height)) + blockHash := common.BytesToHash(block.Hash()) + + testCases := []struct { + name string + blockNum *ethrpc.BlockNumber + hash *common.Hash + registerMock func(*common.Hash) + expPass bool + }{ + { + "error - without blockHash or blockNum", + nil, + nil, + func(hash *common.Hash) {}, + false, + }, + { + "error - with blockHash, tendermint client failed to get block", + nil, + &blockHash, + func(hash *common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, *hash, bz) + }, + false, + }, + { + "pass - with blockHash", + nil, + &blockHash, + func(hash *common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, *hash, bz) + }, + true, + }, + { + "pass - without blockHash & with blockNumber", + &blockNum, + nil, + func(hash *common.Hash) {}, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + blockNrOrHash := ethrpc.BlockNumberOrHash{ + BlockNumber: tc.blockNum, + BlockHash: tc.hash, + } + + tc.registerMock(tc.hash) + blockNum, err := suite.backend.BlockNumberFromTendermint(blockNrOrHash) + + if tc.expPass { + suite.Require().NoError(err) + if tc.hash == nil { + suite.Require().Equal(*tc.blockNum, blockNum) + } else { + expHeight := ethrpc.NewBlockNumber(big.NewInt(resBlock.Block.Height)) + suite.Require().Equal(expHeight, blockNum) + } + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestBlockNumberFromTendermintByHash() { + var resBlock *tmrpctypes.ResultBlock + + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + hash common.Hash + registerMock func(common.Hash) + expPass bool + }{ + { + "fail - tendermint client failed to get block", + common.BytesToHash(block.Hash()), + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, bz) + }, + false, + }, + { + "pass - block without tx", + common.BytesToHash(emptyBlock.Hash()), + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, hash, bz) + }, + true, + }, + { + "pass - block with tx", + common.BytesToHash(block.Hash()), + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, hash, bz) + }, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.hash) + blockNum, err := suite.backend.BlockNumberFromTendermintByHash(tc.hash) + if tc.expPass { + expHeight := big.NewInt(resBlock.Block.Height) + suite.Require().NoError(err) + suite.Require().Equal(expHeight, blockNum) + } else { + suite.Require().Error(err) + } + }) + } +} + func (suite *BackendTestSuite) TestBlockBloom() { testCases := []struct { name string @@ -512,7 +844,7 @@ func (suite *BackendTestSuite) TestBlockBloom() { false, }, { - "pass - nonblock bloom attribute key", + "pass - block bloom attribute key", &tmrpctypes.ResultBlockResults{ EndBlockEvents: []types.Event{ { @@ -541,7 +873,7 @@ func (suite *BackendTestSuite) TestBlockBloom() { } } -func (suite *BackendTestSuite) TestEthBlockFromTendermint() { +func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { msgEthereumTx, bz := suite.buildEthereumTx() emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) @@ -735,7 +1067,7 @@ func (suite *BackendTestSuite) TestEthBlockFromTendermint() { suite.SetupTest() // reset test and queries tc.registerMock(sdk.NewIntFromBigInt(tc.baseFee), tc.validator, tc.height) - block, err := suite.backend.EthBlockFromTendermint(tc.resBlock, tc.blockRes, tc.fullTx) + block, err := suite.backend.RPCBlockFromTendermintBlock(tc.resBlock, tc.blockRes, tc.fullTx) var expBlock map[string]interface{} header := tc.resBlock.Block.Header @@ -785,131 +1117,283 @@ func (suite *BackendTestSuite) TestEthBlockFromTendermint() { } } -func (suite *BackendTestSuite) TestBaseFee() { - baseFee := sdk.NewInt(1) +func (suite *BackendTestSuite) TestEthMsgsFromTendermintBlock() { + msgEthereumTx, bz := suite.buildEthereumTx() testCases := []struct { - name string - blockRes *tmrpctypes.ResultBlockResults - registerMock func() - expBaseFee *big.Int - expPass bool + name string + resBlock *tmrpctypes.ResultBlock + blockRes *tmrpctypes.ResultBlockResults + expMsgs []*evmtypes.MsgEthereumTx }{ { - "fail - grpc BaseFee error", - &tmrpctypes.ResultBlockResults{Height: 1}, - func() { - queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) - RegisterBaseFeeError(queryClient) + "tx in not included in block - unsuccessful tx without ExceedBlockGasLimit error", + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), }, - nil, - false, - }, - { - "fail - grpc BaseFee error - with non feemarket block event", &tmrpctypes.ResultBlockResults{ - Height: 1, - BeginBlockEvents: []types.Event{ + TxsResults: []*types.ResponseDeliverTx{ { - Type: evmtypes.EventTypeBlockBloom, + Code: 1, }, }, }, - func() { - queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) - RegisterBaseFeeError(queryClient) - }, - nil, - false, + []*evmtypes.MsgEthereumTx(nil), }, { - "fail - grpc BaseFee error - with feemarket block event", + "tx included in block - unsuccessful tx with ExceedBlockGasLimit error", + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, &tmrpctypes.ResultBlockResults{ - Height: 1, - BeginBlockEvents: []types.Event{ + TxsResults: []*types.ResponseDeliverTx{ { - Type: feemarkettypes.EventTypeFeeMarket, + Code: 1, + Log: ethrpc.ExceedBlockGasLimitError, }, }, }, - func() { - queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) - RegisterBaseFeeError(queryClient) - }, - nil, - false, + []*evmtypes.MsgEthereumTx{msgEthereumTx}, }, { - "fail - grpc BaseFee error - with feemarket block event with wrong attribute value", + "pass", + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, &tmrpctypes.ResultBlockResults{ - Height: 1, - BeginBlockEvents: []types.Event{ + TxsResults: []*types.ResponseDeliverTx{ { - Type: feemarkettypes.EventTypeFeeMarket, - Attributes: []types.EventAttribute{ - {Value: []byte{0x1}}, - }, + Code: 0, + Log: ethrpc.ExceedBlockGasLimitError, }, }, }, - func() { + []*evmtypes.MsgEthereumTx{msgEthereumTx}, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + msgs := suite.backend.EthMsgsFromTendermintBlock(tc.resBlock, tc.blockRes) + suite.Require().Equal(tc.expMsgs, msgs) + }) + } +} + +func (suite *BackendTestSuite) TestHeaderByNumber() { + var expResultBlock *tmrpctypes.ResultBlock + + _, bz := suite.buildEthereumTx() + + testCases := []struct { + name string + blockNumber ethrpc.BlockNumber + baseFee *big.Int + registerMock func(ethrpc.BlockNumber, sdk.Int) + expPass bool + }{ + { + "fail - tendermint client failed to get block", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdk.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, height) + }, + false, + }, + { + "fail - block not found for height", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdk.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockNotFound(client, height) + }, + false, + }, + { + "fail - block not found for height", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdk.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + RegisterBlockResultsError(client, height) + }, + false, + }, + { + "pass - without Base Fee, failed to fetch from prunned block", + ethrpc.BlockNumber(1), + nil, + func(blockNum ethrpc.BlockNumber, baseFee sdk.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, height, nil) + RegisterBlockResults(client, height) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) RegisterBaseFeeError(queryClient) }, - nil, + true, + }, + { + "pass - blockNum = 1, without tx", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdk.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, height, nil) + RegisterBlockResults(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + true, + }, + { + "pass - blockNum = 1, with tx", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdk.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, height, bz) + RegisterBlockResults(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.blockNumber, sdk.NewIntFromBigInt(tc.baseFee)) + header, err := suite.backend.HeaderByNumber(tc.blockNumber) + + if tc.expPass { + expHeader := ethrpc.EthHeaderFromTendermint(expResultBlock.Block.Header, ethtypes.Bloom{}, tc.baseFee) + suite.Require().NoError(err) + suite.Require().Equal(expHeader, header) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestHeaderByHash() { + var expResultBlock *tmrpctypes.ResultBlock + + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + hash common.Hash + baseFee *big.Int + registerMock func(common.Hash, sdk.Int) + expPass bool + }{ + { + "fail - tendermint client failed to get block", + common.BytesToHash(block.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdk.Int) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, bz) + }, false, }, { - "fail - grpc baseFee error - with feemarket block event with baseFee attribute value", - &tmrpctypes.ResultBlockResults{ - Height: 1, - BeginBlockEvents: []types.Event{ - { - Type: feemarkettypes.EventTypeFeeMarket, - Attributes: []types.EventAttribute{ - {Value: []byte(baseFee.String())}, - }, - }, - }, + "fail - block not found for height", + common.BytesToHash(block.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdk.Int) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashNotFound(client, hash, bz) }, - func() { + false, + }, + { + "fail - block not found for height", + common.BytesToHash(block.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdk.Int) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, bz) + RegisterBlockResultsError(client, height) + }, + false, + }, + { + "pass - without Base Fee, failed to fetch from prunned block", + common.BytesToHash(block.Hash()), + nil, + func(hash common.Hash, baseFee sdk.Int) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlockByHash(client, hash, bz) + RegisterBlockResults(client, height) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) RegisterBaseFeeError(queryClient) }, - baseFee.BigInt(), true, }, { - "fail - base fee or london fork not enabled", - &tmrpctypes.ResultBlockResults{Height: 1}, - func() { + "pass - blockNum = 1, without tx", + common.BytesToHash(emptyBlock.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdk.Int) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlockByHash(client, hash, nil) + RegisterBlockResults(client, height) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) - RegisterBaseFeeDisabled(queryClient) + RegisterBaseFee(queryClient, baseFee) }, - nil, true, }, { - "pass", - &tmrpctypes.ResultBlockResults{Height: 1}, - func() { + "pass - with tx", + common.BytesToHash(block.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdk.Int) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlockByHash(client, hash, bz) + RegisterBlockResults(client, height) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) RegisterBaseFee(queryClient, baseFee) }, - baseFee.BigInt(), true, }, } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { suite.SetupTest() // reset test and queries - tc.registerMock() - baseFee, err := suite.backend.BaseFee(tc.blockRes) + tc.registerMock(tc.hash, sdk.NewIntFromBigInt(tc.baseFee)) + header, err := suite.backend.HeaderByHash(tc.hash) if tc.expPass { + expHeader := ethrpc.EthHeaderFromTendermint(expResultBlock.Block.Header, ethtypes.Bloom{}, tc.baseFee) suite.Require().NoError(err) - suite.Require().Equal(tc.expBaseFee, baseFee) + suite.Require().Equal(expHeader, header) } else { suite.Require().Error(err) } @@ -917,66 +1401,211 @@ func (suite *BackendTestSuite) TestBaseFee() { } } -func (suite *BackendTestSuite) TestGetEthereumMsgsFromTendermintBlock() { +func (suite *BackendTestSuite) TestEthBlockByNumber() { msgEthereumTx, bz := suite.buildEthereumTx() + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) testCases := []struct { - name string - resBlock *tmrpctypes.ResultBlock - blockRes *tmrpctypes.ResultBlockResults - expMsgs []*evmtypes.MsgEthereumTx + name string + blockNumber ethrpc.BlockNumber + registerMock func(ethrpc.BlockNumber) + expEthBlock *ethtypes.Block + expPass bool }{ { - "tx in not included in block - unsuccessful tx without ExceedBlockGasLimit error", - &tmrpctypes.ResultBlock{ - Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + "fail - tendermint client failed to get block", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, height) }, - &tmrpctypes.ResultBlockResults{ - TxsResults: []*types.ResponseDeliverTx{ - { - Code: 1, - }, - }, + nil, + false, + }, + { + "fail - block result not found for height", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + RegisterBlockResultsError(client, blockNum.Int64()) }, - []*evmtypes.MsgEthereumTx(nil), + nil, + false, }, { - "tx included in block - unsuccessful tx with ExceedBlockGasLimit error", + "pass - block without tx", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + + RegisterBlockResults(client, blockNum.Int64()) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + baseFee := sdk.NewInt(1) + RegisterBaseFee(queryClient, baseFee) + }, + ethtypes.NewBlock( + ethrpc.EthHeaderFromTendermint( + emptyBlock.Header, + ethtypes.Bloom{}, + sdk.NewInt(1).BigInt(), + ), + []*ethtypes.Transaction{}, + nil, + nil, + nil, + ), + true, + }, + { + "pass - block with tx", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, bz) + + RegisterBlockResults(client, blockNum.Int64()) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + baseFee := sdk.NewInt(1) + RegisterBaseFee(queryClient, baseFee) + }, + ethtypes.NewBlock( + ethrpc.EthHeaderFromTendermint( + emptyBlock.Header, + ethtypes.Bloom{}, + sdk.NewInt(1).BigInt(), + ), + []*ethtypes.Transaction{msgEthereumTx.AsTransaction()}, + nil, + nil, + trie.NewStackTrie(nil), + ), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock(tc.blockNumber) + + ethBlock, err := suite.backend.EthBlockByNumber(tc.blockNumber) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expEthBlock.Header(), ethBlock.Header()) + suite.Require().Equal(tc.expEthBlock.Uncles(), ethBlock.Uncles()) + suite.Require().Equal(tc.expEthBlock.ReceiptHash(), ethBlock.ReceiptHash()) + for i, tx := range tc.expEthBlock.Transactions() { + suite.Require().Equal(tx.Data(), ethBlock.Transactions()[i].Data()) + } + + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestEthBlockFromTendermintBlock() { + msgEthereumTx, bz := suite.buildEthereumTx() + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + baseFee *big.Int + resBlock *tmrpctypes.ResultBlock + blockRes *tmrpctypes.ResultBlockResults + registerMock func(sdk.Int, int64) + expEthBlock *ethtypes.Block + expPass bool + }{ + { + "pass - block without tx", + sdk.NewInt(1).BigInt(), &tmrpctypes.ResultBlock{ - Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + Block: emptyBlock, }, &tmrpctypes.ResultBlockResults{ - TxsResults: []*types.ResponseDeliverTx{ - { - Code: 1, - Log: ethrpc.ExceedBlockGasLimitError, - }, - }, + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, }, - []*evmtypes.MsgEthereumTx{msgEthereumTx}, + func(baseFee sdk.Int, blockNum int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + ethtypes.NewBlock( + ethrpc.EthHeaderFromTendermint( + emptyBlock.Header, + ethtypes.Bloom{}, + sdk.NewInt(1).BigInt(), + ), + []*ethtypes.Transaction{}, + nil, + nil, + nil, + ), + true, }, { - "pass", + "pass - block with tx", + sdk.NewInt(1).BigInt(), &tmrpctypes.ResultBlock{ Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), }, &tmrpctypes.ResultBlockResults{ - TxsResults: []*types.ResponseDeliverTx{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + EndBlockEvents: []types.Event{ { - Code: 0, - Log: ethrpc.ExceedBlockGasLimitError, + Type: evmtypes.EventTypeBlockBloom, + Attributes: []types.EventAttribute{ + {Key: []byte(bAttributeKeyEthereumBloom)}, + }, }, }, }, - []*evmtypes.MsgEthereumTx{msgEthereumTx}, + func(baseFee sdk.Int, blockNum int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + ethtypes.NewBlock( + ethrpc.EthHeaderFromTendermint( + emptyBlock.Header, + ethtypes.Bloom{}, + sdk.NewInt(1).BigInt(), + ), + []*ethtypes.Transaction{msgEthereumTx.AsTransaction()}, + nil, + nil, + trie.NewStackTrie(nil), + ), + true, }, } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { suite.SetupTest() // reset test and queries + tc.registerMock(sdk.NewIntFromBigInt(tc.baseFee), tc.blockRes.Height) - msgs := suite.backend.GetEthereumMsgsFromTendermintBlock(tc.resBlock, tc.blockRes) - suite.Require().Equal(tc.expMsgs, msgs) + ethBlock, err := suite.backend.EthBlockFromTendermintBlock(tc.resBlock, tc.blockRes) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expEthBlock.Header(), ethBlock.Header()) + suite.Require().Equal(tc.expEthBlock.Uncles(), ethBlock.Uncles()) + suite.Require().Equal(tc.expEthBlock.ReceiptHash(), ethBlock.ReceiptHash()) + for i, tx := range tc.expEthBlock.Transactions() { + suite.Require().Equal(tx.Data(), ethBlock.Transactions()[i].Data()) + } + + } else { + suite.Require().Error(err) + } }) } } diff --git a/rpc/backend/call_tx.go b/rpc/backend/call_tx.go index 87c6ec7215..96932f92c0 100644 --- a/rpc/backend/call_tx.go +++ b/rpc/backend/call_tx.go @@ -361,3 +361,32 @@ func (b *Backend) DoCall( return res, nil } + +// GasPrice returns the current gas price based on Ethermint's gas price oracle. +func (b *Backend) GasPrice() (*hexutil.Big, error) { + var ( + result *big.Int + err error + ) + if head := b.CurrentHeader(); head.BaseFee != nil { + result, err = b.SuggestGasTipCap(head.BaseFee) + if err != nil { + return nil, err + } + result = result.Add(result, head.BaseFee) + } else { + result = big.NewInt(b.RPCMinGasPrice()) + } + + // return at least GlobalMinGasPrice from FeeMarket module + minGasPrice, err := b.GlobalMinGasPrice() + if err != nil { + return nil, err + } + minGasPriceInt := minGasPrice.TruncateInt().BigInt() + if result.Cmp(minGasPriceInt) < 0 { + result = minGasPriceInt + } + + return (*hexutil.Big)(result), nil +} diff --git a/rpc/backend/chain_info.go b/rpc/backend/chain_info.go index 8672b2c6fc..33954aa23a 100644 --- a/rpc/backend/chain_info.go +++ b/rpc/backend/chain_info.go @@ -183,7 +183,7 @@ func (b *Backend) FeeHistory( for blockID := blockStart; blockID < blockEnd; blockID++ { index := int32(blockID - blockStart) // tendermint block - tendermintblock, err := b.GetTendermintBlockByNumber(rpctypes.BlockNumber(blockID)) + tendermintblock, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(blockID)) if tendermintblock == nil { return nil, err } @@ -195,7 +195,7 @@ func (b *Backend) FeeHistory( } // tendermint block result - tendermintBlockResult, err := b.GetTendermintBlockResultByNumber(&tendermintblock.Block.Height) + tendermintBlockResult, err := b.TendermintBlockResultByNumber(&tendermintblock.Block.Height) if tendermintBlockResult == nil { b.logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error()) return nil, err diff --git a/rpc/backend/chain_info_test.go b/rpc/backend/chain_info_test.go new file mode 100644 index 0000000000..4b47902cb7 --- /dev/null +++ b/rpc/backend/chain_info_test.go @@ -0,0 +1,146 @@ +package backend + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/abci/types" + tmrpctypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/evmos/ethermint/rpc/backend/mocks" + evmtypes "github.com/evmos/ethermint/x/evm/types" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" +) + +func (suite *BackendTestSuite) TestBaseFee() { + baseFee := sdk.NewInt(1) + + testCases := []struct { + name string + blockRes *tmrpctypes.ResultBlockResults + registerMock func() + expBaseFee *big.Int + expPass bool + }{ + { + "fail - grpc BaseFee error", + &tmrpctypes.ResultBlockResults{Height: 1}, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFeeError(queryClient) + }, + nil, + false, + }, + { + "fail - grpc BaseFee error - with non feemarket block event", + &tmrpctypes.ResultBlockResults{ + Height: 1, + BeginBlockEvents: []types.Event{ + { + Type: evmtypes.EventTypeBlockBloom, + }, + }, + }, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFeeError(queryClient) + }, + nil, + false, + }, + { + "fail - grpc BaseFee error - with feemarket block event", + &tmrpctypes.ResultBlockResults{ + Height: 1, + BeginBlockEvents: []types.Event{ + { + Type: feemarkettypes.EventTypeFeeMarket, + }, + }, + }, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFeeError(queryClient) + }, + nil, + false, + }, + { + "fail - grpc BaseFee error - with feemarket block event with wrong attribute value", + &tmrpctypes.ResultBlockResults{ + Height: 1, + BeginBlockEvents: []types.Event{ + { + Type: feemarkettypes.EventTypeFeeMarket, + Attributes: []types.EventAttribute{ + {Value: []byte{0x1}}, + }, + }, + }, + }, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFeeError(queryClient) + }, + nil, + false, + }, + { + "fail - grpc baseFee error - with feemarket block event with baseFee attribute value", + &tmrpctypes.ResultBlockResults{ + Height: 1, + BeginBlockEvents: []types.Event{ + { + Type: feemarkettypes.EventTypeFeeMarket, + Attributes: []types.EventAttribute{ + {Value: []byte(baseFee.String())}, + }, + }, + }, + }, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFeeError(queryClient) + }, + baseFee.BigInt(), + true, + }, + { + "fail - base fee or london fork not enabled", + &tmrpctypes.ResultBlockResults{Height: 1}, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFeeDisabled(queryClient) + }, + nil, + true, + }, + { + "pass", + &tmrpctypes.ResultBlockResults{Height: 1}, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.QueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + baseFee.BigInt(), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + baseFee, err := suite.backend.BaseFee(tc.blockRes) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expBaseFee, baseFee) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/rpc/backend/client_test.go b/rpc/backend/client_test.go index edf4efb410..6fbe8b05e5 100644 --- a/rpc/backend/client_test.go +++ b/rpc/backend/client_test.go @@ -145,3 +145,13 @@ func RegisterBlockByHash( Return(resBlock, nil) return resBlock, nil } + +func RegisterBlockByHashError(client *mocks.Client, hash common.Hash, tx []byte) { + client.On("BlockByHash", rpc.ContextWithHeight(1), []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}). + Return(nil, sdkerrors.ErrInvalidRequest) +} + +func RegisterBlockByHashNotFound(client *mocks.Client, hash common.Hash, tx []byte) { + client.On("BlockByHash", rpc.ContextWithHeight(1), []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}). + Return(nil, nil) +} diff --git a/rpc/backend/filters.go b/rpc/backend/filters.go index 5af090446a..bf0b81f866 100644 --- a/rpc/backend/filters.go +++ b/rpc/backend/filters.go @@ -8,7 +8,7 @@ import ( // GetLogs returns all the logs from all the ethereum transactions in a block. func (b *Backend) GetLogs(hash common.Hash) ([][]*ethtypes.Log, error) { - resBlock, err := b.GetTendermintBlockByHash(hash) + resBlock, err := b.TendermintBlockByHash(hash) if err != nil { return nil, err } @@ -22,7 +22,7 @@ func (b *Backend) GetLogs(hash common.Hash) ([][]*ethtypes.Log, error) { // GetLogsByHeight returns all the logs from all the ethereum transactions in a block. func (b *Backend) GetLogsByHeight(height *int64) ([][]*ethtypes.Log, error) { // NOTE: we query the state in case the tx result logs are not persisted after an upgrade. - blockRes, err := b.GetTendermintBlockResultByNumber(height) + blockRes, err := b.TendermintBlockResultByNumber(height) if err != nil { return nil, err } diff --git a/rpc/backend/tracing.go b/rpc/backend/tracing.go index af546ad3b3..8517188ebc 100644 --- a/rpc/backend/tracing.go +++ b/rpc/backend/tracing.go @@ -26,7 +26,7 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfi return nil, errors.New("genesis is not traceable") } - blk, err := b.GetTendermintBlockByNumber(rpctypes.BlockNumber(transaction.Height)) + blk, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(transaction.Height)) if err != nil { b.logger.Debug("block not found", "height", transaction.Height) return nil, err diff --git a/rpc/backend/tx_info.go b/rpc/backend/tx_info.go index a75e6015c3..08f3af7b5b 100644 --- a/rpc/backend/tx_info.go +++ b/rpc/backend/tx_info.go @@ -25,7 +25,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac return b.getTransactionByHashPending(txHash) } - block, err := b.GetTendermintBlockByNumber(rpctypes.BlockNumber(res.Height)) + block, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(res.Height)) if err != nil { return nil, err } @@ -41,7 +41,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac return nil, errors.New("invalid ethereum tx") } - blockRes, err := b.GetTendermintBlockResultByNumber(&block.Block.Height) + blockRes, err := b.TendermintBlockResultByNumber(&block.Block.Height) if err != nil { b.logger.Debug("block result not found", "height", block.Block.Height, "error", err.Error()) return nil, nil @@ -49,7 +49,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac if res.EthTxIndex == -1 { // Fallback to find tx index by iterating all valid eth transactions - msgs := b.GetEthereumMsgsFromTendermintBlock(block, blockRes) + msgs := b.EthMsgsFromTendermintBlock(block, blockRes) for i := range msgs { if msgs[i].Hash == hexTx { res.EthTxIndex = int32(i) @@ -125,7 +125,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ return nil, nil } - resBlock, err := b.GetTendermintBlockByNumber(rpctypes.BlockNumber(res.Height)) + resBlock, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(res.Height)) if err != nil { b.logger.Debug("block not found", "height", res.Height, "error", err.Error()) return nil, nil @@ -144,7 +144,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ } cumulativeGasUsed := uint64(0) - blockRes, err := b.GetTendermintBlockResultByNumber(&res.Height) + blockRes, err := b.TendermintBlockResultByNumber(&res.Height) if err != nil { b.logger.Debug("failed to retrieve block results", "height", res.Height, "error", err.Error()) return nil, nil @@ -179,7 +179,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ if res.EthTxIndex == -1 { // Fallback to find tx index by iterating all valid eth transactions - msgs := b.GetEthereumMsgsFromTendermintBlock(resBlock, blockRes) + msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) for i := range msgs { if msgs[i].Hash == hexTx { res.EthTxIndex = int32(i) @@ -260,7 +260,7 @@ func (b *Backend) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexuti func (b *Backend) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) { b.logger.Debug("eth_getTransactionByBlockNumberAndIndex", "number", blockNum, "index", idx) - block, err := b.GetTendermintBlockByNumber(blockNum) + block, err := b.TendermintBlockByNumber(blockNum) if err != nil { b.logger.Debug("block not found", "height", blockNum.Int64(), "error", err.Error()) return nil, nil @@ -341,7 +341,7 @@ func (b *Backend) queryTendermintTxIndexer(query string, txGetter func(*rpctypes // getTransactionByBlockAndIndex is the common code shared by `GetTransactionByBlockNumberAndIndex` and `GetTransactionByBlockHashAndIndex`. func (b *Backend) GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) { - blockRes, err := b.GetTendermintBlockResultByNumber(&block.Block.Height) + blockRes, err := b.TendermintBlockResultByNumber(&block.Block.Height) if err != nil { return nil, nil } @@ -365,7 +365,7 @@ func (b *Backend) GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, i } } else { i := int(idx) - ethMsgs := b.GetEthereumMsgsFromTendermintBlock(block, blockRes) + ethMsgs := b.EthMsgsFromTendermintBlock(block, blockRes) if i >= len(ethMsgs) { b.logger.Debug("block txs index out of bound", "index", i) return nil, nil diff --git a/rpc/namespaces/ethereum/debug/api.go b/rpc/namespaces/ethereum/debug/api.go index 2ae1bdb7af..d858769b55 100644 --- a/rpc/namespaces/ethereum/debug/api.go +++ b/rpc/namespaces/ethereum/debug/api.go @@ -74,7 +74,7 @@ func (a *API) TraceBlockByNumber(height rpctypes.BlockNumber, config *evmtypes.T return nil, errors.New("genesis is not traceable") } // Get Tendermint Block - resBlock, err := a.backend.GetTendermintBlockByNumber(height) + resBlock, err := a.backend.TendermintBlockByNumber(height) if err != nil { a.logger.Debug("get block failed", "height", height, "error", err.Error()) return nil, err @@ -88,7 +88,7 @@ func (a *API) TraceBlockByNumber(height rpctypes.BlockNumber, config *evmtypes.T func (a *API) TraceBlockByHash(hash common.Hash, config *evmtypes.TraceConfig) ([]*evmtypes.TxTraceResult, error) { a.logger.Debug("debug_traceBlockByHash", "hash", hash) // Get Tendermint Block - resBlock, err := a.backend.GetTendermintBlockByHash(hash) + resBlock, err := a.backend.TendermintBlockByHash(hash) if err != nil { a.logger.Debug("get block failed", "hash", hash.Hex(), "error", err.Error()) return nil, err @@ -298,7 +298,7 @@ func (a *API) GetHeaderRlp(number uint64) (hexutil.Bytes, error) { // GetBlockRlp retrieves the RLP encoded for of a single block. func (a *API) GetBlockRlp(number uint64) (hexutil.Bytes, error) { - block, err := a.backend.BlockByNumber(rpctypes.BlockNumber(number)) + block, err := a.backend.EthBlockByNumber(rpctypes.BlockNumber(number)) if err != nil { return nil, err } @@ -308,7 +308,7 @@ func (a *API) GetBlockRlp(number uint64) (hexutil.Bytes, error) { // PrintBlock retrieves a block and returns its pretty printed form. func (a *API) PrintBlock(number uint64) (string, error) { - block, err := a.backend.BlockByNumber(rpctypes.BlockNumber(number)) + block, err := a.backend.EthBlockByNumber(rpctypes.BlockNumber(number)) if err != nil { return "", err } diff --git a/rpc/namespaces/ethereum/eth/api.go b/rpc/namespaces/ethereum/eth/api.go index 48f48c56de..c58a3357e8 100644 --- a/rpc/namespaces/ethereum/eth/api.go +++ b/rpc/namespaces/ethereum/eth/api.go @@ -2,7 +2,6 @@ package eth import ( "context" - "math/big" "github.com/ethereum/go-ethereum/signer/core/apitypes" @@ -34,6 +33,8 @@ type EthereumAPI interface { BlockNumber() (hexutil.Uint64, error) GetBlockByNumber(ethBlockNum rpctypes.BlockNumber, fullTx bool) (map[string]interface{}, error) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) + GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint + GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumber) *hexutil.Uint // Reading Transactions // @@ -42,8 +43,6 @@ type EthereumAPI interface { GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransaction, error) GetTransactionCount(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Uint64, error) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) - GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint - GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumber) *hexutil.Uint GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) // eth_getBlockReceipts @@ -123,10 +122,7 @@ type PublicAPI struct { } // NewPublicAPI creates an instance of the public ETH Web3 API. -func NewPublicAPI( - logger log.Logger, - backend backend.EVMBackend, -) *PublicAPI { +func NewPublicAPI(logger log.Logger, backend backend.EVMBackend) *PublicAPI { api := &PublicAPI{ ctx: context.Background(), logger: logger.With("client", "json-rpc"), @@ -171,7 +167,7 @@ func (e *PublicAPI) GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransac // GetTransactionCount returns the number of transactions at the given address up to the given block number. func (e *PublicAPI) GetTransactionCount(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Uint64, error) { e.logger.Debug("eth_getTransactionCount", "address", address.Hex(), "block number or hash", blockNrOrHash) - blockNum, err := e.backend.GetBlockNumber(blockNrOrHash) + blockNum, err := e.backend.BlockNumberFromTendermint(blockNrOrHash) if err != nil { return nil, err } @@ -182,21 +178,18 @@ func (e *PublicAPI) GetTransactionCount(address common.Address, blockNrOrHash rp func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { hexTx := hash.Hex() e.logger.Debug("eth_getTransactionReceipt", "hash", hexTx) - return e.backend.GetTransactionReceipt(hash) } // GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash. func (e *PublicAPI) GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint { e.logger.Debug("eth_getBlockTransactionCountByHash", "hash", hash.Hex()) - return e.backend.GetBlockTransactionCountByHash(hash) } // GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number. func (e *PublicAPI) GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumber) *hexutil.Uint { e.logger.Debug("eth_getBlockTransactionCountByNumber", "height", blockNum.Int64()) - return e.backend.GetBlockTransactionCountByNumber(blockNum) } @@ -209,19 +202,7 @@ func (e *PublicAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexu // GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index. func (e *PublicAPI) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) { e.logger.Debug("eth_getTransactionByBlockNumberAndIndex", "number", blockNum, "index", idx) - - block, err := e.backend.GetTendermintBlockByNumber(blockNum) - if err != nil { - e.logger.Debug("block not found", "height", blockNum.Int64(), "error", err.Error()) - return nil, nil - } - - if block.Block == nil { - e.logger.Debug("block not found", "height", blockNum.Int64()) - return nil, nil - } - - return e.backend.GetTransactionByBlockAndIndex(block, idx) + return e.backend.GetTransactionByBlockNumberAndIndex(blockNum, idx) } /////////////////////////////////////////////////////////////////////////////// @@ -288,7 +269,7 @@ func (e *PublicAPI) Call(args evmtypes.TransactionArgs, ) (hexutil.Bytes, error) { e.logger.Debug("eth_call", "args", args.String(), "block number or hash", blockNrOrHash) - blockNum, err := e.backend.GetBlockNumber(blockNrOrHash) + blockNum, err := e.backend.BlockNumberFromTendermint(blockNrOrHash) if err != nil { return nil, err } @@ -303,10 +284,10 @@ func (e *PublicAPI) Call(args evmtypes.TransactionArgs, /////////////////////////////////////////////////////////////////////////////// /// Event Logs /// /////////////////////////////////////////////////////////////////////////////// -// FILTER API +// FILTER API at ./filters/api.go /////////////////////////////////////////////////////////////////////////////// -/// Chain Information /// +/// Chain Information /// /////////////////////////////////////////////////////////////////////////////// // ProtocolVersion returns the supported Ethereum protocol version. @@ -318,31 +299,7 @@ func (e *PublicAPI) ProtocolVersion() hexutil.Uint { // GasPrice returns the current gas price based on Ethermint's gas price oracle. func (e *PublicAPI) GasPrice() (*hexutil.Big, error) { e.logger.Debug("eth_gasPrice") - var ( - result *big.Int - err error - ) - if head := e.backend.CurrentHeader(); head.BaseFee != nil { - result, err = e.backend.SuggestGasTipCap(head.BaseFee) - if err != nil { - return nil, err - } - result = result.Add(result, head.BaseFee) - } else { - result = big.NewInt(e.backend.RPCMinGasPrice()) - } - - // return at least GlobalMinGasPrice from FeeMarket module - minGasPrice, err := e.backend.GlobalMinGasPrice() - if err != nil { - return nil, err - } - minGasPriceInt := minGasPrice.TruncateInt().BigInt() - if result.Cmp(minGasPriceInt) < 0 { - result = minGasPriceInt - } - - return (*hexutil.Big)(result), nil + return e.backend.GasPrice() } // EstimateGas returns an estimate of gas usage for the given smart contract call. @@ -466,7 +423,7 @@ func (e *PublicAPI) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, err return nil, nil } - resBlockResult, err := e.backend.GetTendermintBlockResultByNumber(&res.Height) + resBlockResult, err := e.backend.TendermintBlockResultByNumber(&res.Height) if err != nil { e.logger.Debug("block result not found", "number", res.Height, "error", err.Error()) return nil, nil diff --git a/rpc/namespaces/ethereum/eth/filters/api.go b/rpc/namespaces/ethereum/eth/filters/api.go index bf41baf09c..500349050e 100644 --- a/rpc/namespaces/ethereum/eth/filters/api.go +++ b/rpc/namespaces/ethereum/eth/filters/api.go @@ -39,8 +39,8 @@ type Backend interface { GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map[string]interface{}, error) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, error) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) - GetTendermintBlockByHash(hash common.Hash) (*coretypes.ResultBlock, error) - GetTendermintBlockResultByNumber(height *int64) (*coretypes.ResultBlockResults, error) + TendermintBlockByHash(hash common.Hash) (*coretypes.ResultBlock, error) + TendermintBlockResultByNumber(height *int64) (*coretypes.ResultBlockResults, error) GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error) GetLogsByHeight(*int64) ([][]*ethtypes.Log, error) BlockBloom(blockRes *coretypes.ResultBlockResults) (ethtypes.Bloom, error) diff --git a/rpc/namespaces/ethereum/eth/filters/filters.go b/rpc/namespaces/ethereum/eth/filters/filters.go index e01a0c0444..f8fe9bb7f9 100644 --- a/rpc/namespaces/ethereum/eth/filters/filters.go +++ b/rpc/namespaces/ethereum/eth/filters/filters.go @@ -98,12 +98,12 @@ func (f *Filter) Logs(ctx context.Context, logLimit int, blockLimit int64) ([]*e // If we're doing singleton block filtering, execute and return if f.criteria.BlockHash != nil && *f.criteria.BlockHash != (common.Hash{}) { - resBlock, err := f.backend.GetTendermintBlockByHash(*f.criteria.BlockHash) + resBlock, err := f.backend.TendermintBlockByHash(*f.criteria.BlockHash) if err != nil { return nil, fmt.Errorf("failed to fetch header by hash %s: %w", f.criteria.BlockHash, err) } - blockRes, err := f.backend.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + blockRes, err := f.backend.TendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { f.logger.Debug("failed to fetch block result from Tendermint", "height", resBlock.Block.Height, "error", err.Error()) return nil, nil @@ -155,7 +155,7 @@ func (f *Filter) Logs(ctx context.Context, logLimit int, blockLimit int64) ([]*e to := f.criteria.ToBlock.Int64() for height := from; height <= to; height++ { - blockRes, err := f.backend.GetTendermintBlockResultByNumber(&height) + blockRes, err := f.backend.TendermintBlockResultByNumber(&height) if err != nil { f.logger.Debug("failed to fetch block result from Tendermint", "height", height, "error", err.Error()) return nil, nil