From 89a2e6165f97402f03aad69f5940d06556550801 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 16:21:21 +0700 Subject: [PATCH 01/13] format code, re-group related information --- rpc/backend/backend_suite_test.go | 4 +--- rpc/backend/blocks.go | 9 ++++++--- rpc/backend/blocks_test.go | 4 +--- rpc/types/utils.go | 9 ++++++--- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/rpc/backend/backend_suite_test.go b/rpc/backend/backend_suite_test.go index c3e28a1b0b..55b0624877 100644 --- a/rpc/backend/backend_suite_test.go +++ b/rpc/backend/backend_suite_test.go @@ -155,12 +155,10 @@ func (suite *BackendTestSuite) buildFormattedBlock( return rpctypes.FormatBlock( header, resBlock.Block.Size(), - gasLimit, - gasUsed, + gasLimit, gasUsed, baseFee, ethRPCTxs, bloom, common.BytesToAddress(validator.Bytes()), - baseFee, ) } diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index 4823e2e900..7c6fc84fa8 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -453,9 +453,12 @@ func (b *Backend) RPCBlockFromTendermintBlock( } formattedBlock := rpctypes.FormatBlock( - block.Header, block.Size(), - gasLimit, new(big.Int).SetUint64(gasUsed), - ethRPCTxs, bloom, validatorAddr, baseFee, + block.Header, + block.Size(), + gasLimit, new(big.Int).SetUint64(gasUsed), baseFee, + ethRPCTxs, + bloom, + validatorAddr, ) return formattedBlock, nil } diff --git a/rpc/backend/blocks_test.go b/rpc/backend/blocks_test.go index 9f499f8d94..44a6b6664e 100644 --- a/rpc/backend/blocks_test.go +++ b/rpc/backend/blocks_test.go @@ -1113,12 +1113,10 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { expBlock = ethrpc.FormatBlock( header, tc.resBlock.Block.Size(), - gasLimit, - gasUsed, + gasLimit, gasUsed, tc.baseFee, ethRPCTxs, bloom, common.BytesToAddress(tc.validator.Bytes()), - tc.baseFee, ) if tc.expPass { diff --git a/rpc/types/utils.go b/rpc/types/utils.go index 5abe0376de..215772d3ab 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -102,9 +102,12 @@ func BlockMaxGasFromConsensusParams(goCtx context.Context, clientCtx client.Cont // FormatBlock creates an ethereum block from a tendermint header and ethereum-formatted // transactions. func FormatBlock( - header tmtypes.Header, size int, gasLimit int64, - gasUsed *big.Int, transactions []interface{}, bloom ethtypes.Bloom, - validatorAddr common.Address, baseFee *big.Int, + header tmtypes.Header, + size int, + gasLimit int64, gasUsed *big.Int, baseFee *big.Int, + transactions []interface{}, + bloom ethtypes.Bloom, + validatorAddr common.Address, ) map[string]interface{} { var transactionsRoot common.Hash if len(transactions) == 0 { From 0379fb4825ed250032993ce55619da39f6941664 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 16:51:20 +0700 Subject: [PATCH 02/13] * fix bug transaction root was computed incorrectly --- rpc/backend/backend_suite_test.go | 21 +++-------- rpc/backend/blocks.go | 30 +++------------ rpc/backend/blocks_test.go | 22 +++-------- .../eth_rpc_it_suite/eth_api_block_test.go | 2 +- rpc/types/utils.go | 37 +++++++++++++++++-- 5 files changed, 52 insertions(+), 60 deletions(-) diff --git a/rpc/backend/backend_suite_test.go b/rpc/backend/backend_suite_test.go index 55b0624877..6b53076392 100644 --- a/rpc/backend/backend_suite_test.go +++ b/rpc/backend/backend_suite_test.go @@ -134,31 +134,20 @@ func (suite *BackendTestSuite) buildFormattedBlock( receipt := ethtypes.NewReceipt(root, false, gasUsed.Uint64()) bloom := ethtypes.CreateBloom(ethtypes.Receipts{receipt}) - ethRPCTxs := []interface{}{} + var transactions ethtypes.Transactions if tx != nil { - if fullTx { - rpcTx, err := rpctypes.NewRPCTransaction( - tx.AsTransaction(), - common.BytesToHash(header.Hash()), - uint64(header.Height), - uint64(0), - baseFee, - suite.backend.chainID, - ) - suite.Require().NoError(err) - ethRPCTxs = []interface{}{rpcTx} - } else { - ethRPCTxs = []interface{}{common.HexToHash(tx.Hash)} - } + transactions = append(transactions, tx.AsTransaction()) } return rpctypes.FormatBlock( header, + suite.backend.chainID, resBlock.Block.Size(), gasLimit, gasUsed, baseFee, - ethRPCTxs, + transactions, fullTx, bloom, common.BytesToAddress(validator.Bytes()), + suite.backend.logger, ) } diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index 7c6fc84fa8..92f6215998 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -370,7 +370,6 @@ func (b *Backend) RPCBlockFromTendermintBlock( blockRes *tmrpctypes.ResultBlockResults, fullTx bool, ) (map[string]interface{}, error) { - ethRPCTxs := []interface{}{} block := resBlock.Block baseFee, err := b.BaseFee(blockRes) @@ -380,29 +379,10 @@ func (b *Backend) RPCBlockFromTendermintBlock( } msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) - for txIndex, ethMsg := range msgs { - if !fullTx { - hash := common.HexToHash(ethMsg.Hash) - ethRPCTxs = append(ethRPCTxs, hash) - continue - } - tx := ethMsg.AsTransaction() - height := uint64(block.Height) //#nosec G701 -- checked for int overflow already - index := uint64(txIndex) //#nosec G701 -- checked for int overflow already - rpcTx, err := rpctypes.NewRPCTransaction( - tx, - common.BytesToHash(block.Hash()), - height, - index, - baseFee, - b.chainID, - ) - if err != nil { - b.logger.Debug("NewTransactionFromData for receipt failed", "hash", tx.Hash().Hex(), "error", err.Error()) - continue - } - ethRPCTxs = append(ethRPCTxs, rpcTx) + var transactions ethtypes.Transactions + for _, ethMsg := range msgs { + transactions = append(transactions, ethMsg.AsTransaction()) } bloom, err := b.BlockBloom(blockRes) @@ -454,11 +434,13 @@ func (b *Backend) RPCBlockFromTendermintBlock( formattedBlock := rpctypes.FormatBlock( block.Header, + b.chainID, block.Size(), gasLimit, new(big.Int).SetUint64(gasUsed), baseFee, - ethRPCTxs, + transactions, fullTx, bloom, validatorAddr, + b.logger, ) return formattedBlock, nil } diff --git a/rpc/backend/blocks_test.go b/rpc/backend/blocks_test.go index 44a6b6664e..2820e7d401 100644 --- a/rpc/backend/blocks_test.go +++ b/rpc/backend/blocks_test.go @@ -2,6 +2,7 @@ package backend import ( "fmt" + "github.com/cometbft/cometbft/libs/log" "math/big" "cosmossdk.io/math" @@ -1091,32 +1092,21 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { receipt := ethtypes.NewReceipt(root, false, gasUsed.Uint64()) bloom := ethtypes.CreateBloom(ethtypes.Receipts{receipt}) - ethRPCTxs := []interface{}{} + var transactions ethtypes.Transactions if tc.expTxs { - if tc.fullTx { - rpcTx, err := ethrpc.NewRPCTransaction( - msgEthereumTx.AsTransaction(), - common.BytesToHash(header.Hash()), - uint64(header.Height), - uint64(0), - tc.baseFee, - suite.backend.chainID, - ) - suite.Require().NoError(err) - ethRPCTxs = []interface{}{rpcTx} - } else { - ethRPCTxs = []interface{}{common.HexToHash(msgEthereumTx.Hash)} - } + transactions = append(transactions, msgEthereumTx.AsTransaction()) } expBlock = ethrpc.FormatBlock( header, + suite.backend.chainID, tc.resBlock.Block.Size(), gasLimit, gasUsed, tc.baseFee, - ethRPCTxs, + transactions, tc.fullTx, bloom, common.BytesToAddress(tc.validator.Bytes()), + log.NewNopLogger(), ) if tc.expPass { diff --git a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_test.go b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_test.go index 09c4d3693c..304727f9d9 100644 --- a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_test.go +++ b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_test.go @@ -365,7 +365,7 @@ func (suite *EthRpcTestSuite) Test_GetBlockByNumberAndHash() { suite.Equal(fmt.Sprintf("0x%x", blockResult.Block.Time.UTC().Unix()), textResultStruct.Timestamp, "timestamp must be block UTC epoch seconds") suite.Equal("0x0", textResultStruct.TotalDifficulty, "total difficulty must be zero since PoS chain does not have this") suite.Len(textResultStruct.Transactions, evmTxsCount, "transaction list must be same as sent EVM txs") - suite.Equal(func() string { // TODO ES fix the RPC to return correct transaction root + suite.Equal(func() string { var transactions ethtypes.Transactions for _, tx := range textResultStruct.Transactions { var transaction *ethtypes.Transaction diff --git a/rpc/types/utils.go b/rpc/types/utils.go index 215772d3ab..b627a701a5 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -3,7 +3,9 @@ package types import ( "context" "fmt" + "github.com/cometbft/cometbft/libs/log" tmrpcclient "github.com/cometbft/cometbft/rpc/client" + "github.com/ethereum/go-ethereum/trie" "math/big" "strings" @@ -103,17 +105,46 @@ func BlockMaxGasFromConsensusParams(goCtx context.Context, clientCtx client.Cont // transactions. func FormatBlock( header tmtypes.Header, + chainID *big.Int, size int, gasLimit int64, gasUsed *big.Int, baseFee *big.Int, - transactions []interface{}, + transactions ethtypes.Transactions, fullTx bool, bloom ethtypes.Bloom, validatorAddr common.Address, + logger log.Logger, ) map[string]interface{} { var transactionsRoot common.Hash if len(transactions) == 0 { transactionsRoot = ethtypes.EmptyRootHash } else { - transactionsRoot = common.BytesToHash(header.DataHash) + transactionsRoot = ethtypes.DeriveSha(transactions, trie.NewStackTrie(nil)) + } + + var txsList []interface{} + + for txIndex, tx := range transactions { + if !fullTx { + txsList = append(txsList, tx.Hash()) + continue + } + + height := uint64(header.Height) //#nosec G701 -- checked for int overflow already + index := uint64(txIndex) //#nosec G701 -- checked for int overflow already + + rpcTx, err := NewRPCTransaction( + tx, + common.BytesToHash(header.Hash()), + height, + index, + baseFee, + chainID, + ) + if err != nil { + logger.Error("NewRPCTransaction failed", "hash", tx.Hash().Hex(), "error", err.Error()) + continue + } + + txsList = append(txsList, rpcTx) } result := map[string]interface{}{ @@ -136,7 +167,7 @@ func FormatBlock( "receiptsRoot": ethtypes.EmptyRootHash, "uncles": []common.Hash{}, - "transactions": transactions, + "transactions": txsList, "totalDifficulty": (*hexutil.Big)(big.NewInt(0)), } From 296d52088cbfb6839c1877f805c13398b4b75bdb Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 17:32:54 +0700 Subject: [PATCH 03/13] extract RPC receipt to a dedicated type instead of the dynamic map --- rpc/backend/backend.go | 2 +- rpc/backend/tx_info.go | 56 +++++++++---------- rpc/backend/tx_info_test.go | 8 +-- rpc/namespaces/ethereum/eth/api.go | 4 +- .../eth_api_transaction_test.go | 10 ++-- rpc/types/types.go | 29 ++++++++++ 6 files changed, 66 insertions(+), 43 deletions(-) diff --git a/rpc/backend/backend.go b/rpc/backend/backend.go index f4b6372c0b..b17f06aa39 100644 --- a/rpc/backend/backend.go +++ b/rpc/backend/backend.go @@ -103,7 +103,7 @@ type EVMBackend interface { GetTxByEthHash(txHash common.Hash) (*evertypes.TxResult, error) GetTxByTxIndex(height int64, txIndex uint) (*evertypes.TxResult, error) GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) - GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) + GetTransactionReceipt(hash common.Hash) (*rpctypes.RPCReceipt, error) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) diff --git a/rpc/backend/tx_info.go b/rpc/backend/tx_info.go index a57857aac0..74cefa713f 100644 --- a/rpc/backend/tx_info.go +++ b/rpc/backend/tx_info.go @@ -137,7 +137,7 @@ func (b *Backend) GetGasUsed(res *types.TxResult, price *big.Int, gas uint64) ui } // GetTransactionReceipt returns the transaction receipt identified by hash. -func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { +func (b *Backend) GetTransactionReceipt(hash common.Hash) (*rpctypes.RPCReceipt, error) { hexTx := hash.Hex() b.logger.Debug("eth_getTransactionReceipt", "hash", hexTx) @@ -213,38 +213,31 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ return nil, errors.New("can't find index of ethereum tx") } - receipt := map[string]interface{}{ - // Consensus fields: These fields are defined by the Yellow Paper - "status": status, - "cumulativeGasUsed": hexutil.Uint64(cumulativeGasUsed), - "logsBloom": ethtypes.BytesToBloom(ethtypes.LogsBloom(logs)), - "logs": logs, - - // Implementation fields: These fields are added by geth when processing a transaction. - // They are stored in the chain database. - "transactionHash": hash, - "contractAddress": nil, - "gasUsed": hexutil.Uint64(b.GetGasUsed(res, txData.GetGasPrice(), txData.GetGas())), - - // Inclusion information: These fields provide information about the inclusion of the - // transaction corresponding to this receipt. - "blockHash": common.BytesToHash(resBlock.Block.Header.Hash()).Hex(), - "blockNumber": hexutil.Uint64(res.Height), - "transactionIndex": hexutil.Uint64(res.EthTxIndex), - - // sender and receiver (contract or EOA) addreses - "from": from, - "to": txData.GetTo(), - "type": hexutil.Uint(ethMsg.AsTransaction().Type()), + if logs == nil { + logs = []*ethtypes.Log{} } - if logs == nil { - receipt["logs"] = [][]*ethtypes.Log{} + var rpcReceipt rpctypes.RPCReceipt + rpcReceipt = rpctypes.RPCReceipt{ + Status: status, + CumulativeGasUsed: hexutil.Uint64(cumulativeGasUsed), + Bloom: ethtypes.BytesToBloom(ethtypes.LogsBloom(logs)), + Logs: logs, + TransactionHash: hash, + ContractAddress: nil, + GasUsed: hexutil.Uint64(b.GetGasUsed(res, txData.GetGasPrice(), txData.GetGas())), + BlockHash: common.BytesToHash(resBlock.BlockID.Hash.Bytes()), + BlockNumber: hexutil.Uint64(res.Height), + TransactionIndex: hexutil.Uint64(res.EthTxIndex), + Type: hexutil.Uint(ethMsg.AsTransaction().Type()), + From: from, + To: txData.GetTo(), + EffectiveGasPrice: nil, } - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation - if txData.GetTo() == nil { - receipt["contractAddress"] = crypto.CreateAddress(from, txData.GetNonce()) + if rpcReceipt.To == nil { + newContractAddress := crypto.CreateAddress(from, txData.GetNonce()) + rpcReceipt.ContractAddress = &newContractAddress } if dynamicTx, ok := txData.(*evmtypes.DynamicFeeTx); ok { @@ -253,11 +246,12 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ // tolerate the error for pruned node. b.logger.Error("fetch basefee failed, node is pruned?", "height", res.Height, "error", err) } else { - receipt["effectiveGasPrice"] = hexutil.Big(*dynamicTx.EffectiveGasPrice(baseFee)) + effectiveGasPrice := hexutil.Big(*dynamicTx.EffectiveGasPrice(baseFee)) + rpcReceipt.EffectiveGasPrice = &effectiveGasPrice } } - return receipt, nil + return &rpcReceipt, nil } // GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index. diff --git a/rpc/backend/tx_info_test.go b/rpc/backend/tx_info_test.go index a084ff00ed..db8e6f861e 100644 --- a/rpc/backend/tx_info_test.go +++ b/rpc/backend/tx_info_test.go @@ -554,7 +554,7 @@ func (suite *BackendTestSuite) TestGetTransactionReceipt() { tx *evmtypes.MsgEthereumTx block *types.Block blockResult []*abci.ResponseDeliverTx - expTxReceipt map[string]interface{} + expTxReceipt *rpctypes.RPCReceipt expPass bool }{ { @@ -587,7 +587,7 @@ func (suite *BackendTestSuite) TestGetTransactionReceipt() { }, }, }, - map[string]interface{}(nil), + (*rpctypes.RPCReceipt)(nil), false, }, } @@ -605,9 +605,9 @@ func (suite *BackendTestSuite) TestGetTransactionReceipt() { txReceipt, err := suite.backend.GetTransactionReceipt(common.HexToHash(tc.tx.Hash)) if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(txReceipt, tc.expTxReceipt) + suite.Equal(tc.expTxReceipt, txReceipt) } else { - suite.Require().NotEqual(txReceipt, tc.expTxReceipt) + suite.NotEqual(tc.expTxReceipt, txReceipt) } }) } diff --git a/rpc/namespaces/ethereum/eth/api.go b/rpc/namespaces/ethereum/eth/api.go index f610d6abd1..068e43c0d0 100644 --- a/rpc/namespaces/ethereum/eth/api.go +++ b/rpc/namespaces/ethereum/eth/api.go @@ -42,7 +42,7 @@ type EthereumAPI interface { // it is a user or a smart contract. 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) + GetTransactionReceipt(hash common.Hash) (*rpctypes.RPCReceipt, error) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) // eth_getBlockReceipts @@ -175,7 +175,7 @@ func (e *PublicAPI) GetTransactionCount(address common.Address, blockNrOrHash rp } // GetTransactionReceipt returns the transaction receipt identified by hash. -func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { +func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (*rpctypes.RPCReceipt, error) { hexTx := hash.Hex() e.logger.Debug("eth_getTransactionReceipt", "hash", hexTx) return e.backend.GetTransactionReceipt(hash) diff --git a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_test.go b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_test.go index 8db272a680..883015b3c6 100644 --- a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_test.go +++ b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_test.go @@ -365,16 +365,16 @@ func (suite *EthRpcTestSuite) Test_GetTransactionReceipt() { } suite.Empty(receipt.Logs) suite.Equal(sentTxHash, receipt.TxHash) - suite.Nil(gotReceipt["contractAddress"]) + suite.Nil(gotReceipt.ContractAddress) suite.Greater(receipt.GasUsed, uint64(0)) suite.Equal(*gotTx.BlockHash, receipt.BlockHash) suite.Equal(gotTx.BlockNumber.ToInt().Int64(), receipt.BlockNumber.Int64()) suite.Equal(uint(*gotTx.TransactionIndex), receipt.TransactionIndex) - if suite.NotNil(gotReceipt["from"]) { - suite.Equal(sender.GetEthAddress(), gotReceipt["from"].(common.Address)) + if suite.NotNil(gotReceipt.From) { + suite.Equal(sender.GetEthAddress(), gotReceipt.From) } - if suite.NotNil(gotReceipt["to"]) { - suite.Equal(receiver.GetEthAddress(), *(gotReceipt["to"].(*common.Address))) + if suite.NotNil(gotReceipt.To) { + suite.Equal(receiver.GetEthAddress(), *(gotReceipt.To)) } suite.Equal(sentEvmTx.AsTransaction().Type(), receipt.Type) }) diff --git a/rpc/types/types.go b/rpc/types/types.go index df44e50d62..4b01b4b3f8 100644 --- a/rpc/types/types.go +++ b/rpc/types/types.go @@ -52,6 +52,35 @@ type RPCTransaction struct { S *hexutil.Big `json:"s"` } +// RPCReceipt represents a receipt (of a transaction) that will serialize to the RPC representation of the receipt +type RPCReceipt struct { + // Consensus fields: These fields are defined by the Yellow Paper + Status hexutil.Uint `json:"status"` + CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed"` + Bloom ethtypes.Bloom `json:"logsBloom"` + Logs []*ethtypes.Log `json:"logs"` + + // Implementation fields: These fields are added by geth when processing a transaction. + // They are stored in the chain database. + TransactionHash common.Hash `json:"transactionHash"` + ContractAddress *common.Address `json:"contractAddress,omitempty"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + + // Inclusion information: These fields provide information about the inclusion of the + // transaction corresponding to this receipt. + BlockHash common.Hash `json:"blockHash"` + BlockNumber hexutil.Uint64 `json:"blockNumber"` + TransactionIndex hexutil.Uint64 `json:"transactionIndex"` + Type hexutil.Uint `json:"type"` + + // sender and receiver (contract or EOA) addresses + From common.Address `json:"from"` + To *common.Address `json:"to"` + + // others + EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice,omitempty"` +} + // StateOverride is the collection of overridden accounts. type StateOverride map[common.Address]OverrideAccount From 5c4d840b72d7f620cfec5937c4b5c45b78515277 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 17:44:40 +0700 Subject: [PATCH 04/13] update test for getting transaction receipt --- .../eth_api_transaction_test.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_test.go b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_test.go index 883015b3c6..c4167bccff 100644 --- a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_test.go +++ b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_test.go @@ -329,6 +329,27 @@ func (suite *EthRpcTestSuite) Test_GetTransactionCount() { } func (suite *EthRpcTestSuite) Test_GetTransactionReceipt() { + assertReceiptFields := func(receipt *rpctypes.RPCReceipt) { + if receipt == nil { + return + } + + bz, err := json.Marshal(receipt) + suite.Require().NoError(err) + + var mapper map[string]interface{} + err = json.Unmarshal(bz, &mapper) + suite.Require().NoError(err) + + logs, found := mapper["logs"] + if suite.True(found, "expected logs always available regardless empty or not") { + arr, ok := logs.([]interface{}) + if suite.True(ok) { + suite.Equal(len(receipt.Logs), len(arr)) + } + } + } + suite.Run("basic", func() { sender := suite.CITS.WalletAccounts.Number(1) receiver := suite.CITS.WalletAccounts.Number(2) @@ -350,6 +371,7 @@ func (suite *EthRpcTestSuite) Test_GetTransactionReceipt() { gotReceipt, err := suite.GetEthPublicAPI().GetTransactionReceipt(sentTxHash) suite.Require().NoError(err) suite.Require().NotNil(gotReceipt) + assertReceiptFields(gotReceipt) bzReceipt, err := json.Marshal(gotReceipt) suite.Require().NoError(err) @@ -431,6 +453,7 @@ func (suite *EthRpcTestSuite) Test_GetTransactionReceipt() { gotReceipt, err := suite.GetEthPublicAPI().GetTransactionReceipt(sentTxHash) suite.Require().NoError(err) suite.Require().NotNil(gotReceipt) + assertReceiptFields(gotReceipt) bzReceipt, err := json.Marshal(gotReceipt) suite.Require().NoError(err) @@ -459,6 +482,7 @@ func (suite *EthRpcTestSuite) Test_GetTransactionReceipt() { bzReceipt, err := json.Marshal(gotReceipt) suite.Require().NoError(err) + assertReceiptFields(gotReceipt) var receipt ethtypes.Receipt err = json.Unmarshal(bzReceipt, &receipt) @@ -480,6 +504,7 @@ func (suite *EthRpcTestSuite) Test_GetTransactionReceipt() { gotReceipt, err := suite.GetEthPublicAPI().GetTransactionReceipt(sentTxHash) suite.Require().NoError(err) suite.Require().NotNil(gotReceipt) + assertReceiptFields(gotReceipt) bzReceipt, err := json.Marshal(gotReceipt) suite.Require().NoError(err) From 25bc494005e32e711c2edb9f5e8ea87018887260 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 20:12:18 +0700 Subject: [PATCH 05/13] refactor RPCBlockFromTendermintBlock without changing any logic --- rpc/backend/blocks.go | 54 ++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index 92f6215998..60a8098b48 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -370,25 +370,9 @@ func (b *Backend) RPCBlockFromTendermintBlock( blockRes *tmrpctypes.ResultBlockResults, fullTx bool, ) (map[string]interface{}, error) { - block := resBlock.Block - - baseFee, err := b.BaseFee(blockRes) - if err != nil { - // handle the error for pruned node. - b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", block.Height, "error", err) - } - - msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) + // prepare block information - var transactions ethtypes.Transactions - for _, ethMsg := range msgs { - transactions = append(transactions, ethMsg.AsTransaction()) - } - - bloom, err := b.BlockBloom(blockRes) - if err != nil { - b.logger.Debug("failed to query BlockBloom", "height", block.Height, "error", err.Error()) - } + block := resBlock.Block req := &evmtypes.QueryValidatorAccountRequest{ ConsAddress: sdk.ConsAddress(block.Header.ProposerAddress).String(), @@ -399,6 +383,7 @@ func (b *Backend) RPCBlockFromTendermintBlock( ctx := rpctypes.ContextWithHeight(block.Height) res, err := b.queryClient.ValidatorAccount(ctx, req) if err != nil { + // TODO ES return error b.logger.Debug( "failed to query validator operator address", "height", block.Height, @@ -406,6 +391,7 @@ func (b *Backend) RPCBlockFromTendermintBlock( "error", err.Error(), ) // use zero address as the validator operator address + //goland:noinspection GoRedundantConversion validatorAccAddr = sdk.AccAddress(common.Address{}.Bytes()) } else { validatorAccAddr, err = sdk.AccAddressFromBech32(res.AccountAddress) @@ -416,12 +402,15 @@ func (b *Backend) RPCBlockFromTendermintBlock( validatorAddr := common.BytesToAddress(validatorAccAddr) + // prepare gas & fee information + gasLimit, err := rpctypes.BlockMaxGasFromConsensusParams(ctx, b.clientCtx, block.Height) if err != nil { + // TODO ES return error b.logger.Error("failed to query consensus params", "error", err.Error()) } - gasUsed := uint64(0) + var gasUsed uint64 for _, txsResult := range blockRes.TxsResults { // workaround for cosmos-sdk bug. https://github.com/cosmos/cosmos-sdk/issues/10832 @@ -432,6 +421,32 @@ func (b *Backend) RPCBlockFromTendermintBlock( gasUsed += uint64(txsResult.GetGasUsed()) // #nosec G701 -- checked for int overflow already } + baseFee, err := b.BaseFee(blockRes) + if err != nil { + // TODO ES return error + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from pruned block. Check node pruning configuration", "height", block.Height, "error", err) + } + + // prepare txs information + + ethMsgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) + + var transactions ethtypes.Transactions + for _, ethMsg := range ethMsgs { + transactions = append(transactions, ethMsg.AsTransaction()) + } + + // prepare bloom information + + bloom, err := b.BlockBloom(blockRes) + if err != nil { + // TODO ES return error + b.logger.Debug("failed to query BlockBloom", "height", block.Height, "error", err.Error()) + } + + // finalize + formattedBlock := rpctypes.FormatBlock( block.Header, b.chainID, @@ -442,6 +457,7 @@ func (b *Backend) RPCBlockFromTendermintBlock( validatorAddr, b.logger, ) + return formattedBlock, nil } From b22eb2a8f92f719dbf7ef61cd165f7bae89b762d Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 21:32:06 +0700 Subject: [PATCH 06/13] refactor GetTransactionReceipt extract ulti NewRPCReceipt without changing any logic --- rpc/backend/tx_info.go | 74 +++++++++++++++--------------------------- rpc/types/utils.go | 70 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 48 deletions(-) diff --git a/rpc/backend/tx_info.go b/rpc/backend/tx_info.go index 74cefa713f..819cb43a5f 100644 --- a/rpc/backend/tx_info.go +++ b/rpc/backend/tx_info.go @@ -16,7 +16,6 @@ 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/crypto" "github.com/pkg/errors" ) @@ -127,13 +126,17 @@ func (b *Backend) getTransactionByHashPending(txHash common.Hash) (*rpctypes.RPC // GetGasUsed returns gasUsed from transaction func (b *Backend) GetGasUsed(res *types.TxResult, price *big.Int, gas uint64) uint64 { + return b.getGasUsed(!res.Failed, res.Height, price, gas, res.GasUsed) +} + +func (b *Backend) getGasUsed(success bool, height int64, price *big.Int, gas, recordedGasUsed uint64) uint64 { // patch gasUsed if tx is reverted and happened before height on which fixed was introduced // to return real gas charged // more info at https://github.com/evmos/ethermint/pull/1557 - if res.Failed && res.Height < b.cfg.JSONRPC.FixRevertGasRefundHeight { + if !success && height < b.cfg.JSONRPC.FixRevertGasRefundHeight { return new(big.Int).Mul(price, new(big.Int).SetUint64(gas)).Uint64() } - return res.GasUsed + return recordedGasUsed } // GetTransactionReceipt returns the transaction receipt identified by hash. @@ -146,16 +149,19 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (*rpctypes.RPCReceipt, b.logger.Debug("tx not found", "hash", hexTx, "error", err.Error()) return nil, nil } + 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 } + tx, err := b.clientCtx.TxConfig.TxDecoder()(resBlock.Block.Txs[res.TxIndex]) if err != nil { b.logger.Debug("decoding failed", "error", err.Error()) return nil, fmt.Errorf("failed to decode tx: %w", err) } + ethMsg := tx.GetMsgs()[res.MsgIndex].(*evmtypes.MsgEthereumTx) txData, err := evmtypes.UnpackTxData(ethMsg.Data) @@ -175,26 +181,16 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (*rpctypes.RPCReceipt, } cumulativeGasUsed += res.CumulativeGasUsed - var status hexutil.Uint - if res.Failed { - status = hexutil.Uint(ethtypes.ReceiptStatusFailed) - } else { - status = hexutil.Uint(ethtypes.ReceiptStatusSuccessful) - } chainID, err := b.ChainID() if err != nil { return nil, err } - from, err := ethMsg.GetSender(chainID.ToInt()) - if err != nil { - return nil, err - } - // parse tx logs from events msgIndex := int(res.MsgIndex) // #nosec G701 -- checked for int overflow already logs, err := TxLogsFromEvents(blockRes.TxsResults[res.TxIndex].Events, msgIndex) if err != nil { + // TODO ES return error b.logger.Debug("failed to parse logs", "hash", hexTx, "error", err.Error()) } @@ -213,45 +209,29 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (*rpctypes.RPCReceipt, return nil, errors.New("can't find index of ethereum tx") } - if logs == nil { - logs = []*ethtypes.Log{} - } - - var rpcReceipt rpctypes.RPCReceipt - rpcReceipt = rpctypes.RPCReceipt{ - Status: status, - CumulativeGasUsed: hexutil.Uint64(cumulativeGasUsed), - Bloom: ethtypes.BytesToBloom(ethtypes.LogsBloom(logs)), - Logs: logs, - TransactionHash: hash, - ContractAddress: nil, - GasUsed: hexutil.Uint64(b.GetGasUsed(res, txData.GetGasPrice(), txData.GetGas())), - BlockHash: common.BytesToHash(resBlock.BlockID.Hash.Bytes()), - BlockNumber: hexutil.Uint64(res.Height), - TransactionIndex: hexutil.Uint64(res.EthTxIndex), - Type: hexutil.Uint(ethMsg.AsTransaction().Type()), - From: from, - To: txData.GetTo(), - EffectiveGasPrice: nil, - } - - if rpcReceipt.To == nil { - newContractAddress := crypto.CreateAddress(from, txData.GetNonce()) - rpcReceipt.ContractAddress = &newContractAddress - } - - if dynamicTx, ok := txData.(*evmtypes.DynamicFeeTx); ok { - baseFee, err := b.BaseFee(blockRes) + var baseFee *big.Int + if ethMsg.AsTransaction().Type() == uint8(ethtypes.DynamicFeeTxType) { + baseFee, err = b.BaseFee(blockRes) if err != nil { + // TODO ES return error + // TODO ES in NewRPCReceipt, remove condition check base fee is nil // tolerate the error for pruned node. b.logger.Error("fetch basefee failed, node is pruned?", "height", res.Height, "error", err) - } else { - effectiveGasPrice := hexutil.Big(*dynamicTx.EffectiveGasPrice(baseFee)) - rpcReceipt.EffectiveGasPrice = &effectiveGasPrice } } - return &rpcReceipt, nil + return rpctypes.NewRPCReceipt( + ethMsg, + hexutil.Uint64(res.EthTxIndex), + !res.Failed, + hexutil.Uint64(b.GetGasUsed(res, txData.GetGasPrice(), txData.GetGas())), + hexutil.Uint64(cumulativeGasUsed), + baseFee, + logs, + common.BytesToHash(resBlock.BlockID.Hash.Bytes()), + hexutil.Uint64(res.Height), + chainID.ToInt(), + ) } // GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index. diff --git a/rpc/types/utils.go b/rpc/types/utils.go index b627a701a5..5f88f86105 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/cometbft/cometbft/libs/log" tmrpcclient "github.com/cometbft/cometbft/rpc/client" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/trie" + "github.com/pkg/errors" "math/big" "strings" @@ -130,7 +132,7 @@ func FormatBlock( height := uint64(header.Height) //#nosec G701 -- checked for int overflow already index := uint64(txIndex) //#nosec G701 -- checked for int overflow already - + rpcTx, err := NewRPCTransaction( tx, common.BytesToHash(header.Hash()), @@ -252,6 +254,72 @@ func NewRPCTransaction( return result, nil } +func NewRPCReceipt( + ethMsg *evmtypes.MsgEthereumTx, + transactionIndex hexutil.Uint64, + success bool, + gasUsed hexutil.Uint64, + cumulativeGasUsed hexutil.Uint64, + baseFee *big.Int, + logs []*ethtypes.Log, + blockHash common.Hash, + blockNumber hexutil.Uint64, + chainID *big.Int, +) (receipt *RPCReceipt, err error) { + var status hexutil.Uint + + if success { + status = hexutil.Uint(ethtypes.ReceiptStatusSuccessful) + } else { + status = hexutil.Uint(ethtypes.ReceiptStatusFailed) + } + + if logs == nil { + logs = []*ethtypes.Log{} + } + + from, err := ethMsg.GetSender(chainID) + if err != nil { + return nil, errors.Wrap(err, "failed to get sender") + } + + txData, err := evmtypes.UnpackTxData(ethMsg.Data) + if err != nil { + return nil, errors.Wrap(err, "failed to unpack tx data") + } + + rpcReceipt := RPCReceipt{ + Status: status, + CumulativeGasUsed: cumulativeGasUsed, + Bloom: ethtypes.BytesToBloom(ethtypes.LogsBloom(logs)), + Logs: logs, + TransactionHash: ethMsg.AsTransaction().Hash(), + ContractAddress: nil, + GasUsed: gasUsed, + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: transactionIndex, + Type: hexutil.Uint(ethMsg.AsTransaction().Type()), + From: from, + To: txData.GetTo(), + EffectiveGasPrice: nil, + } + + if rpcReceipt.To == nil { + newContractAddress := crypto.CreateAddress(from, txData.GetNonce()) + rpcReceipt.ContractAddress = &newContractAddress + } + + if baseFee != nil { + if dynamicTx, ok := txData.(*evmtypes.DynamicFeeTx); ok { + effectiveGasPrice := hexutil.Big(*dynamicTx.EffectiveGasPrice(baseFee)) + rpcReceipt.EffectiveGasPrice = &effectiveGasPrice + } + } + + return &rpcReceipt, nil +} + // BaseFeeFromEvents parses the feemarket basefee from cosmos events func BaseFeeFromEvents(events []abci.Event) *big.Int { for _, event := range events { From 6faa770aa101bb9691670e07bac51d54eaa4fbb6 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 21:40:38 +0700 Subject: [PATCH 07/13] add method to convert RPCReceipt into ethtypes.Receipt without changing any logic --- rpc/types/receipt.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 rpc/types/receipt.go diff --git a/rpc/types/receipt.go b/rpc/types/receipt.go new file mode 100644 index 0000000000..888ae006cd --- /dev/null +++ b/rpc/types/receipt.go @@ -0,0 +1,29 @@ +package types + +import ( + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "math/big" +) + +func (r *RPCReceipt) AsEthReceipt() *ethtypes.Receipt { + var contractAddress common.Address + if r.ContractAddress != nil { + contractAddress = *r.ContractAddress + } + + return ðtypes.Receipt{ + Type: uint8(r.Type), + PostState: nil, + Status: uint64(r.Status), + CumulativeGasUsed: uint64(r.CumulativeGasUsed), + Bloom: r.Bloom, + Logs: r.Logs, + TxHash: r.TransactionHash, + ContractAddress: contractAddress, + GasUsed: uint64(r.GasUsed), + BlockHash: r.BlockHash, + BlockNumber: big.NewInt(int64(r.BlockNumber)), + TransactionIndex: uint(r.TransactionIndex), + } +} From 1175e2879ea14e309f268e53100bcf82643a9f96 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 21:42:52 +0700 Subject: [PATCH 08/13] prepare new input arg `receipts` for `FormatBlock` without changing any logic --- rpc/backend/backend_suite_test.go | 1 + rpc/backend/blocks.go | 1 + rpc/backend/blocks_test.go | 1 + rpc/types/utils.go | 1 + 4 files changed, 4 insertions(+) diff --git a/rpc/backend/backend_suite_test.go b/rpc/backend/backend_suite_test.go index 6b53076392..6462d23413 100644 --- a/rpc/backend/backend_suite_test.go +++ b/rpc/backend/backend_suite_test.go @@ -145,6 +145,7 @@ func (suite *BackendTestSuite) buildFormattedBlock( resBlock.Block.Size(), gasLimit, gasUsed, baseFee, transactions, fullTx, + ethtypes.Receipts{receipt}, bloom, common.BytesToAddress(validator.Bytes()), suite.backend.logger, diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index 60a8098b48..924d786834 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -453,6 +453,7 @@ func (b *Backend) RPCBlockFromTendermintBlock( block.Size(), gasLimit, new(big.Int).SetUint64(gasUsed), baseFee, transactions, fullTx, + nil, bloom, validatorAddr, b.logger, diff --git a/rpc/backend/blocks_test.go b/rpc/backend/blocks_test.go index 2820e7d401..bafdc1412d 100644 --- a/rpc/backend/blocks_test.go +++ b/rpc/backend/blocks_test.go @@ -1104,6 +1104,7 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { tc.resBlock.Block.Size(), gasLimit, gasUsed, tc.baseFee, transactions, tc.fullTx, + ethtypes.Receipts{receipt}, bloom, common.BytesToAddress(tc.validator.Bytes()), log.NewNopLogger(), diff --git a/rpc/types/utils.go b/rpc/types/utils.go index 5f88f86105..f0dfe5f144 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -111,6 +111,7 @@ func FormatBlock( size int, gasLimit int64, gasUsed *big.Int, baseFee *big.Int, transactions ethtypes.Transactions, fullTx bool, + receipts ethtypes.Receipts, bloom ethtypes.Bloom, validatorAddr common.Address, logger log.Logger, From 22b1df3b0939704e04c68ebf44f6edf7b9f36e64 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sat, 13 Jan 2024 22:14:15 +0700 Subject: [PATCH 09/13] prepare receipts to be computed receipt root hash without changing any logic --- rpc/backend/blocks.go | 67 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index 924d786834..ae8fe45977 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -402,6 +402,11 @@ func (b *Backend) RPCBlockFromTendermintBlock( validatorAddr := common.BytesToAddress(validatorAccAddr) + chainID, err := b.ChainID() + if err != nil { + return nil, err + } + // prepare gas & fee information gasLimit, err := rpctypes.BlockMaxGasFromConsensusParams(ctx, b.clientCtx, block.Height) @@ -411,14 +416,18 @@ func (b *Backend) RPCBlockFromTendermintBlock( } var gasUsed uint64 + var gasUsedByTxs []uint64 + for _, txResult := range blockRes.TxsResults { + gasUsedByTx := uint64(txResult.GetGasUsed()) // #nosec G701 -- checked for int overflow already - for _, txsResult := range blockRes.TxsResults { // workaround for cosmos-sdk bug. https://github.com/cosmos/cosmos-sdk/issues/10832 - if ShouldIgnoreGasUsed(txsResult) { + if ShouldIgnoreGasUsed(txResult) { // block gas limit has exceeded, other txs must have failed with same reason. - break + gasUsedByTx = 0 } - gasUsed += uint64(txsResult.GetGasUsed()) // #nosec G701 -- checked for int overflow already + + gasUsed += gasUsedByTx + gasUsedByTxs = append(gasUsedByTxs, gasUsedByTx) } baseFee, err := b.BaseFee(blockRes) @@ -433,11 +442,53 @@ func (b *Backend) RPCBlockFromTendermintBlock( ethMsgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) var transactions ethtypes.Transactions - for _, ethMsg := range ethMsgs { - transactions = append(transactions, ethMsg.AsTransaction()) + var receipts ethtypes.Receipts + for transactionIndex, ethMsg := range ethMsgs { + transaction := ethMsg.AsTransaction() + + transactions = append(transactions, transaction) + + indexedTxByHash, err := b.GetTxByEthHash(transaction.Hash()) + if err != nil { + return nil, err + } + + var cumulativeGasUsed uint64 + for _, gasUsedByTx := range gasUsedByTxs[0:indexedTxByHash.TxIndex] { // previous txs + cumulativeGasUsed += gasUsedByTx + } + cumulativeGasUsed += indexedTxByHash.CumulativeGasUsed + + logs, err := TxLogsFromEvents(blockRes.TxsResults[indexedTxByHash.TxIndex].Events, int(indexedTxByHash.MsgIndex)) + if err != nil { + // TODO ES return error + b.logger.Debug("failed to parse logs", "hash", transaction.Hash().Hex(), "error", err.Error()) + } + + txData, err := evmtypes.UnpackTxData(ethMsg.Data) + if err != nil { + return nil, errors.Wrap(err, "failed to unpack tx data") + } + + receipt, err := rpctypes.NewRPCReceipt( + ethMsg, + hexutil.Uint64(transactionIndex), + !indexedTxByHash.Failed, + hexutil.Uint64(b.GetGasUsed(indexedTxByHash, txData.GetGasPrice(), txData.GetGas())), + hexutil.Uint64(cumulativeGasUsed), + baseFee, + logs, + common.BytesToHash(resBlock.BlockID.Hash.Bytes()), + hexutil.Uint64(indexedTxByHash.Height), + chainID.ToInt(), + ) + if err != nil { + return nil, errors.Wrap(err, "failed to create transaction receipt") + } + receipts = append(receipts, receipt.AsEthReceipt()) } - // prepare bloom information + // prepare block-bloom information bloom, err := b.BlockBloom(blockRes) if err != nil { @@ -453,7 +504,7 @@ func (b *Backend) RPCBlockFromTendermintBlock( block.Size(), gasLimit, new(big.Int).SetUint64(gasUsed), baseFee, transactions, fullTx, - nil, + receipts, bloom, validatorAddr, b.logger, From 73d68f53d5a001500456bcd76d890e148a04f7be Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sun, 14 Jan 2024 00:22:37 +0700 Subject: [PATCH 10/13] setup mock EVMTxIndexer --- rpc/backend/backend_suite_test.go | 1 + rpc/backend/indexer_test.go | 48 ++++++++++ rpc/backend/mocks/indexer.go | 141 ++++++++++++++++++++++++++++++ types/indexer.go | 2 + 4 files changed, 192 insertions(+) create mode 100644 rpc/backend/indexer_test.go create mode 100644 rpc/backend/mocks/indexer.go diff --git a/rpc/backend/backend_suite_test.go b/rpc/backend/backend_suite_test.go index 6462d23413..95108788cc 100644 --- a/rpc/backend/backend_suite_test.go +++ b/rpc/backend/backend_suite_test.go @@ -87,6 +87,7 @@ func (suite *BackendTestSuite) SetupTest() { suite.backend.clientCtx.Client = mocks.NewClient(suite.T()) suite.backend.queryClient.FeeMarket = mocks.NewFeeMarketQueryClient(suite.T()) suite.backend.ctx = rpctypes.ContextWithHeight(1) + suite.backend.indexer = mocks.NewEVMTxIndexer(suite.T()) // Add codec encCfg := encoding.MakeConfig(app.ModuleBasics) diff --git a/rpc/backend/indexer_test.go b/rpc/backend/indexer_test.go new file mode 100644 index 0000000000..48865638a7 --- /dev/null +++ b/rpc/backend/indexer_test.go @@ -0,0 +1,48 @@ +package backend + +import ( + "github.com/EscanBE/evermint/v12/rpc/backend/mocks" + "github.com/EscanBE/evermint/v12/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/common" +) + +// QueryClient defines a mocked object that implements the ethermint GRPC +// QueryClient interface. It allows for performing QueryClient queries without having +// to run a ethermint GRPC server. +// +// To use a mock method it has to be registered in a given test. +var _ types.EVMTxIndexer = &mocks.EVMTxIndexer{} + +const mockGasUsed = 100 + +func RegisterIndexerGetByBlockAndIndex(queryClient *mocks.EVMTxIndexer, height int64, index int32) { + queryClient.On("GetByBlockAndIndex", height, index). + Return(&types.TxResult{ + Height: height, + TxIndex: uint32(index), + MsgIndex: 0, + EthTxIndex: index, + Failed: false, + GasUsed: mockGasUsed, + CumulativeGasUsed: mockGasUsed, + }, nil) +} + +func RegisterIndexerGetByBlockAndIndexError(queryClient *mocks.EVMTxIndexer, height int64, index int32) { + queryClient.On("GetByBlockAndIndex", height, index). + Return(nil, sdkerrors.ErrInvalidRequest) +} + +func RegisterIndexerGetByTxHash(queryClient *mocks.EVMTxIndexer, hash common.Hash, height int64) { + queryClient.On("GetByTxHash", hash). + Return(&types.TxResult{ + Height: height, + TxIndex: 0, + MsgIndex: 0, + EthTxIndex: 0, + Failed: false, + GasUsed: mockGasUsed, + CumulativeGasUsed: mockGasUsed, + }, nil) +} diff --git a/rpc/backend/mocks/indexer.go b/rpc/backend/mocks/indexer.go new file mode 100644 index 0000000000..4f9873e2fe --- /dev/null +++ b/rpc/backend/mocks/indexer.go @@ -0,0 +1,141 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + abcitypes "github.com/cometbft/cometbft/abci/types" + cometbfttypes "github.com/cometbft/cometbft/types" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + types "github.com/EscanBE/evermint/v12/types" +) + +// EVMTxIndexer is an autogenerated mock type for the EVMTxIndexer type +type EVMTxIndexer struct { + mock.Mock +} + +// GetByBlockAndIndex provides a mock function with given fields: _a0, _a1 +func (_m *EVMTxIndexer) GetByBlockAndIndex(_a0 int64, _a1 int32) (*types.TxResult, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for GetByBlockAndIndex") + } + + var r0 *types.TxResult + var r1 error + if rf, ok := ret.Get(0).(func(int64, int32) (*types.TxResult, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(int64, int32) *types.TxResult); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.TxResult) + } + } + + if rf, ok := ret.Get(1).(func(int64, int32) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetByTxHash provides a mock function with given fields: _a0 +func (_m *EVMTxIndexer) GetByTxHash(_a0 common.Hash) (*types.TxResult, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for GetByTxHash") + } + + var r0 *types.TxResult + var r1 error + if rf, ok := ret.Get(0).(func(common.Hash) (*types.TxResult, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(common.Hash) *types.TxResult); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.TxResult) + } + } + + if rf, ok := ret.Get(1).(func(common.Hash) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// IndexBlock provides a mock function with given fields: _a0, _a1 +func (_m *EVMTxIndexer) IndexBlock(_a0 *cometbfttypes.Block, _a1 []*abcitypes.ResponseDeliverTx) error { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for IndexBlock") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*cometbfttypes.Block, []*abcitypes.ResponseDeliverTx) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// LastIndexedBlock provides a mock function with given fields: +func (_m *EVMTxIndexer) LastIndexedBlock() (int64, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LastIndexedBlock") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func() (int64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewEVMTxIndexer interface { + mock.TestingT + Cleanup(func()) +} + +// NewEVMTxIndexer creates a new instance of EVMTxIndexer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEVMTxIndexer(t mockConstructorTestingTNewEVMTxIndexer) *EVMTxIndexer { + mock := &EVMTxIndexer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/types/indexer.go b/types/indexer.go index 132ad7cc6b..f881b5cb6e 100644 --- a/types/indexer.go +++ b/types/indexer.go @@ -1,3 +1,5 @@ +//go:generate mockery --name EVMTxIndexer + package types import ( From 781432cf0e1fe8b1ebc6065ecb8e422a008df3e7 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sun, 14 Jan 2024 00:42:24 +0700 Subject: [PATCH 11/13] register mocks for EVMTxIndexer with new implementation --- rpc/backend/backend_suite_test.go | 19 ++++++++ rpc/backend/blocks_test.go | 76 +++++++++++++++++++++++++++++-- rpc/backend/chain_info_test.go | 7 ++- rpc/backend/tx_info_test.go | 15 +++++- 4 files changed, 110 insertions(+), 7 deletions(-) diff --git a/rpc/backend/backend_suite_test.go b/rpc/backend/backend_suite_test.go index 95108788cc..b09b9235a5 100644 --- a/rpc/backend/backend_suite_test.go +++ b/rpc/backend/backend_suite_test.go @@ -180,3 +180,22 @@ func (suite *BackendTestSuite) signAndEncodeEthTx(msgEthereumTx *evmtypes.MsgEth return txBz } + +func (suite *BackendTestSuite) signMsgEthTx(msgEthereumTx *evmtypes.MsgEthereumTx) (*evmtypes.MsgEthereumTx, []byte) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsWithoutHeader(queryClient, 1) + + ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig()) + msgEthereumTx.From = suite.from.String() + err := msgEthereumTx.Sign(ethSigner, suite.signer) + suite.Require().NoError(err) + + tx, err := msgEthereumTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), constants.BaseDenom) + suite.Require().NoError(err) + + txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder() + txBz, err := txEncoder(tx) + suite.Require().NoError(err) + + return msgEthereumTx, txBz +} diff --git a/rpc/backend/blocks_test.go b/rpc/backend/blocks_test.go index bafdc1412d..22342df4a7 100644 --- a/rpc/backend/blocks_test.go +++ b/rpc/backend/blocks_test.go @@ -86,7 +86,8 @@ func (suite *BackendTestSuite) TestGetBlockByNumber() { blockRes *tmrpctypes.ResultBlockResults resBlock *tmrpctypes.ResultBlock ) - msgEthereumTx, bz := suite.buildEthereumTx() + msgEthereumTx, _ := suite.buildEthereumTx() + msgEthereumTx, bz := suite.signMsgEthTx(msgEthereumTx) testCases := []struct { name string @@ -167,6 +168,10 @@ func (suite *BackendTestSuite) TestGetBlockByNumber() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) RegisterValidatorAccount(queryClient, validator) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) }, false, true, @@ -189,6 +194,13 @@ func (suite *BackendTestSuite) TestGetBlockByNumber() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) RegisterValidatorAccount(queryClient, validator) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByTxHash(indexer, msgEthereumTx.AsTransaction().Hash(), height) }, false, true, @@ -229,7 +241,8 @@ func (suite *BackendTestSuite) TestGetBlockByHash() { blockRes *tmrpctypes.ResultBlockResults resBlock *tmrpctypes.ResultBlock ) - msgEthereumTx, bz := suite.buildEthereumTx() + msgEthereumTx, _ := suite.buildEthereumTx() + msgEthereumTx, bz := suite.signMsgEthTx(msgEthereumTx) block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) @@ -312,6 +325,10 @@ func (suite *BackendTestSuite) TestGetBlockByHash() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) RegisterValidatorAccount(queryClient, validator) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) }, false, true, @@ -335,6 +352,13 @@ func (suite *BackendTestSuite) TestGetBlockByHash() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) RegisterValidatorAccount(queryClient, validator) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByTxHash(indexer, msgEthereumTx.AsTransaction().Hash(), height) }, false, true, @@ -888,7 +912,8 @@ func (suite *BackendTestSuite) TestBlockBloom() { } func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { - msgEthereumTx, bz := suite.buildEthereumTx() + msgEthereumTx, _ := suite.buildEthereumTx() + msgEthereumTx, bz := suite.signMsgEthTx(msgEthereumTx) emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) testCases := []struct { @@ -921,6 +946,10 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { client := suite.backend.clientCtx.Client.(*mocks.Client) RegisterConsensusParams(client, height) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) }, false, true, @@ -945,6 +974,13 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { client := suite.backend.clientCtx.Client.(*mocks.Client) RegisterConsensusParams(client, height) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByTxHash(indexer, msgEthereumTx.AsTransaction().Hash(), height) }, true, true, @@ -969,6 +1005,13 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { client := suite.backend.clientCtx.Client.(*mocks.Client) RegisterConsensusParams(client, height) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByTxHash(indexer, msgEthereumTx.AsTransaction().Hash(), height) }, true, true, @@ -993,6 +1036,13 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { client := suite.backend.clientCtx.Client.(*mocks.Client) RegisterConsensusParamsError(client, height) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByTxHash(indexer, msgEthereumTx.AsTransaction().Hash(), height) }, true, true, @@ -1023,6 +1073,10 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { client := suite.backend.clientCtx.Client.(*mocks.Client) RegisterConsensusParams(client, height) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) }, false, true, @@ -1047,6 +1101,13 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { client := suite.backend.clientCtx.Client.(*mocks.Client) RegisterConsensusParams(client, height) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByTxHash(indexer, msgEthereumTx.AsTransaction().Hash(), height) }, true, true, @@ -1071,6 +1132,13 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { client := suite.backend.clientCtx.Client.(*mocks.Client) RegisterConsensusParams(client, height) + + var header metadata.MD + RegisterParams(queryClient, &header, height) + RegisterParamsWithoutHeader(queryClient, height) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByTxHash(indexer, msgEthereumTx.AsTransaction().Hash(), height) }, true, true, @@ -1111,8 +1179,8 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { ) if tc.expPass { - suite.Require().Equal(expBlock, block) suite.Require().NoError(err) + suite.Equal(expBlock, block) } else { suite.Require().Error(err) } diff --git a/rpc/backend/chain_info_test.go b/rpc/backend/chain_info_test.go index 70485ebd28..0664c8da30 100644 --- a/rpc/backend/chain_info_test.go +++ b/rpc/backend/chain_info_test.go @@ -390,17 +390,20 @@ func (suite *BackendTestSuite) TestFeeHistory() { { "fail - Invalid base fee", func(validator sdk.AccAddress) { - // baseFee := sdk.NewInt(1) queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) client := suite.backend.clientCtx.Client.(*mocks.Client) suite.backend.cfg.JSONRPC.FeeHistoryCap = 2 - _, err := RegisterBlock(client, ethrpc.BlockNumber(1).Int64(), nil) + _, err := RegisterBlock(client, 1, nil) suite.Require().NoError(err) _, err = RegisterBlockResults(client, 1) suite.Require().NoError(err) RegisterBaseFeeError(queryClient) RegisterValidatorAccount(queryClient, validator) RegisterConsensusParams(client, 1) + + var header metadata.MD + RegisterParams(queryClient, &header, 1) + RegisterParamsWithoutHeader(queryClient, 1) }, 1, 1, diff --git a/rpc/backend/tx_info_test.go b/rpc/backend/tx_info_test.go index db8e6f861e..a29ffee866 100644 --- a/rpc/backend/tx_info_test.go +++ b/rpc/backend/tx_info_test.go @@ -281,7 +281,8 @@ func (suite *BackendTestSuite) TestGetTransactionByBlockHashAndIndex() { } func (suite *BackendTestSuite) TestGetTransactionByBlockAndIndex() { - msgEthTx, bz := suite.buildEthereumTx() + msgEthTx, _ := suite.buildEthereumTx() + msgEthTx, bz := suite.signMsgEthTx(msgEthTx) defaultBlock := types.MakeBlock(1, []types.Tx{bz}, nil, nil) defaultResponseDeliverTx := []*abci.ResponseDeliverTx{ @@ -322,6 +323,9 @@ func (suite *BackendTestSuite) TestGetTransactionByBlockAndIndex() { client := suite.backend.clientCtx.Client.(*mocks.Client) _, err := RegisterBlockResults(client, 1) suite.Require().NoError(err) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByBlockAndIndexError(indexer, 1, 1) }, &tmrpctypes.ResultBlock{Block: types.MakeBlock(1, []types.Tx{bz}, nil, nil)}, 1, @@ -336,6 +340,9 @@ func (suite *BackendTestSuite) TestGetTransactionByBlockAndIndex() { _, err := RegisterBlockResults(client, 1) suite.Require().NoError(err) RegisterBaseFeeError(queryClient) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByBlockAndIndex(indexer, 1, 0) }, &tmrpctypes.ResultBlock{Block: defaultBlock}, 0, @@ -370,6 +377,9 @@ func (suite *BackendTestSuite) TestGetTransactionByBlockAndIndex() { _, err := RegisterBlockResults(client, 1) suite.Require().NoError(err) RegisterBaseFee(queryClient, sdk.NewInt(1)) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByBlockAndIndex(indexer, 1, 0) }, &tmrpctypes.ResultBlock{Block: defaultBlock}, 0, @@ -435,6 +445,9 @@ func (suite *BackendTestSuite) TestGetTransactionByBlockNumberAndIndex() { _, err = RegisterBlockResults(client, 1) suite.Require().NoError(err) RegisterBaseFee(queryClient, sdk.NewInt(1)) + + indexer := suite.backend.indexer.(*mocks.EVMTxIndexer) + RegisterIndexerGetByBlockAndIndex(indexer, 1, 0) }, 0, 0, From 062f20c789fe09294a6f524d6e5eff85ee56f830 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sun, 14 Jan 2024 00:47:30 +0700 Subject: [PATCH 12/13] * fix bug receipt root was computed incorrectly --- rpc/backend/backend_suite_test.go | 37 ++++++++++++++++--- rpc/backend/blocks_test.go | 11 +++--- .../eth_rpc_it_suite/eth_api_block_test.go | 2 +- rpc/types/utils.go | 9 ++++- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/rpc/backend/backend_suite_test.go b/rpc/backend/backend_suite_test.go index b09b9235a5..53ff4c51cd 100644 --- a/rpc/backend/backend_suite_test.go +++ b/rpc/backend/backend_suite_test.go @@ -131,28 +131,55 @@ func (suite *BackendTestSuite) buildFormattedBlock( gasLimit := int64(^uint32(0)) // for `MaxGas = -1` (DefaultConsensusParams) gasUsed := new(big.Int).SetUint64(uint64(blockRes.TxsResults[0].GasUsed)) - root := common.Hash{}.Bytes() - receipt := ethtypes.NewReceipt(root, false, gasUsed.Uint64()) - bloom := ethtypes.CreateBloom(ethtypes.Receipts{receipt}) - var transactions ethtypes.Transactions + var receipts ethtypes.Receipts if tx != nil { transactions = append(transactions, tx.AsTransaction()) + receipt := createTestReceipt(nil, resBlock, tx, false, mockGasUsed) + receipts = append(receipts, receipt) } + bloom := ethtypes.CreateBloom(receipts) + return rpctypes.FormatBlock( header, suite.backend.chainID, resBlock.Block.Size(), gasLimit, gasUsed, baseFee, transactions, fullTx, - ethtypes.Receipts{receipt}, + receipts, bloom, common.BytesToAddress(validator.Bytes()), suite.backend.logger, ) } +func createTestReceipt(root []byte, resBlock *tmrpctypes.ResultBlock, tx *evmtypes.MsgEthereumTx, failed bool, gasUsed uint64) *ethtypes.Receipt { + var status uint64 + if failed { + status = ethtypes.ReceiptStatusFailed + } else { + status = ethtypes.ReceiptStatusSuccessful + } + + transaction := tx.AsTransaction() + + return ðtypes.Receipt{ + Type: transaction.Type(), + PostState: root, + Status: status, + CumulativeGasUsed: gasUsed, + Bloom: ethtypes.BytesToBloom(ethtypes.LogsBloom([]*ethtypes.Log{})), + Logs: []*ethtypes.Log{}, + TxHash: transaction.Hash(), + ContractAddress: common.Address{}, + GasUsed: gasUsed, + BlockHash: common.HexToHash(resBlock.Block.Header.Hash().String()), + BlockNumber: big.NewInt(resBlock.Block.Height), + TransactionIndex: 0, + } +} + func (suite *BackendTestSuite) generateTestKeyring(clientDir string) (keyring.Keyring, error) { buf := bufio.NewReader(os.Stdin) encCfg := encoding.MakeConfig(app.ModuleBasics) diff --git a/rpc/backend/blocks_test.go b/rpc/backend/blocks_test.go index 22342df4a7..4cfbbe83d2 100644 --- a/rpc/backend/blocks_test.go +++ b/rpc/backend/blocks_test.go @@ -1156,23 +1156,24 @@ func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { gasLimit := int64(^uint32(0)) // for `MaxGas = -1` (DefaultConsensusParams) gasUsed := new(big.Int).SetUint64(uint64(tc.blockRes.TxsResults[0].GasUsed)) - root := common.Hash{}.Bytes() - receipt := ethtypes.NewReceipt(root, false, gasUsed.Uint64()) - bloom := ethtypes.CreateBloom(ethtypes.Receipts{receipt}) - var transactions ethtypes.Transactions + var receipts ethtypes.Receipts if tc.expTxs { transactions = append(transactions, msgEthereumTx.AsTransaction()) + receipt := createTestReceipt(nil, tc.resBlock, msgEthereumTx, false, mockGasUsed) + receipts = append(receipts, receipt) } + bloom := ethtypes.CreateBloom(receipts) + expBlock = ethrpc.FormatBlock( header, suite.backend.chainID, tc.resBlock.Block.Size(), gasLimit, gasUsed, tc.baseFee, transactions, tc.fullTx, - ethtypes.Receipts{receipt}, + receipts, bloom, common.BytesToAddress(tc.validator.Bytes()), log.NewNopLogger(), diff --git a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_test.go b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_test.go index 304727f9d9..c5e0bb53c7 100644 --- a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_test.go +++ b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_test.go @@ -339,7 +339,7 @@ func (suite *EthRpcTestSuite) Test_GetBlockByNumberAndHash() { suite.Equal("0x0000000000000000", textResultStruct.Nonce, "nonce must be zero since PoS chain does not have this") suite.Equal(fmt.Sprintf("0x%x", testBlockHeight), textResultStruct.Number) suite.Equal("0x"+hex.EncodeToString(previousBlockResult.Block.Hash()), textResultStruct.ParentHash, "parentHash must be previous Tendermint block hash") - suite.Equal(func() string { // TODO ES fix the RPC to return correct receipt root + suite.Equal(func() string { var receipts ethtypes.Receipts for _, tx := range textResultStruct.Transactions { var transaction *ethtypes.Transaction diff --git a/rpc/types/utils.go b/rpc/types/utils.go index f0dfe5f144..4441adec39 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -123,6 +123,13 @@ func FormatBlock( transactionsRoot = ethtypes.DeriveSha(transactions, trie.NewStackTrie(nil)) } + var receiptsRoot common.Hash + if len(receipts) == 0 { + receiptsRoot = ethtypes.EmptyRootHash + } else { + receiptsRoot = ethtypes.DeriveSha(receipts, trie.NewStackTrie(nil)) + } + var txsList []interface{} for txIndex, tx := range transactions { @@ -167,7 +174,7 @@ func FormatBlock( "gasUsed": (*hexutil.Big)(gasUsed), "timestamp": hexutil.Uint64(header.Time.Unix()), "transactionsRoot": transactionsRoot, - "receiptsRoot": ethtypes.EmptyRootHash, + "receiptsRoot": receiptsRoot, "uncles": []common.Hash{}, "transactions": txsList, From 03b72f67ed9dcafe175f00d28892e87712384f0d Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Sun, 14 Jan 2024 01:51:02 +0700 Subject: [PATCH 13/13] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0ca370680..f9da035342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - (ante) [#59](https://github.com/EscanBE/evermint/pull/59) Prevent panic when building error message of fee which overflow int64 - (swagger) [#66](https://github.com/EscanBE/evermint/pull/66) Correct script gen swagger after switched to use vanilla Cosmos-SDK - (rename-chain) [#80](https://github.com/EscanBE/evermint/pull/80) Handle new cases of rename-chain with recent changes +- (rpc) [#85](https://github.com/EscanBE/evermint/pull/85) Compute and return correct `transactionsRoot` and `receiptsRoot` hashes ### Client Breaking