diff --git a/rpc/backend/backend_suite_test.go b/rpc/backend/backend_suite_test.go index 0a230937e1..04709abc2e 100644 --- a/rpc/backend/backend_suite_test.go +++ b/rpc/backend/backend_suite_test.go @@ -115,6 +115,32 @@ func (suite *BackendTestSuite) buildEthereumTx() (*evmtypes.MsgEthereumTx, []byt return msgEthereumTx, bz } +func (suite *BackendTestSuite) buildEthereumTxWithChainId(chainId *big.Int) (*evmtypes.MsgEthereumTx, []byte) { + msgEthereumTx := evmtypes.NewTx( + chainId, + uint64(0), + &common.Address{}, + big.NewInt(0), + 100000, + big.NewInt(1), + nil, + nil, + nil, + nil, + ) + + // A valid msg should have empty `From` + msgEthereumTx.From = "" + + txBuilder := suite.backend.clientCtx.TxConfig.NewTxBuilder() + err := txBuilder.SetMsgs(msgEthereumTx) + suite.Require().NoError(err) + + bz, err := suite.backend.clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + suite.Require().NoError(err) + return msgEthereumTx, bz +} + // buildFormattedBlock returns a formatted block for testing func (suite *BackendTestSuite) buildFormattedBlock( blockRes *tmrpctypes.ResultBlockResults, diff --git a/rpc/backend/tx_info_test.go b/rpc/backend/tx_info_test.go index 976509470e..5cd0fa46fa 100644 --- a/rpc/backend/tx_info_test.go +++ b/rpc/backend/tx_info_test.go @@ -10,11 +10,14 @@ import ( tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" "github.com/cometbft/cometbft/types" dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/server" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/evmos/ethermint/indexer" "github.com/evmos/ethermint/rpc/backend/mocks" rpctypes "github.com/evmos/ethermint/rpc/types" + "github.com/evmos/ethermint/tests" ethermint "github.com/evmos/ethermint/types" evmtypes "github.com/evmos/ethermint/x/evm/types" "google.golang.org/grpc/metadata" @@ -605,6 +608,179 @@ func (suite *BackendTestSuite) TestGetTransactionReceipt() { } } +func (suite *BackendTestSuite) TestCheckChainIdWithTransactionReceipt() { + + patchedHeight := int64(10848200) + notPatchedHeight := int64(10848100) + + // TX using chain id 9000 + from, pk := tests.NewAddrKey() + msgEthereumTx, _ := suite.buildEthereumTxWithChainId(big.NewInt(9000)) + txHash := msgEthereumTx.AsTransaction().Hash() + + signer := tests.NewSigner(pk) + ethSigner := ethtypes.NewLondonSigner(big.NewInt(9000)) + msgEthereumTx.From = from.String() + err := msgEthereumTx.Sign(ethSigner, signer) + suite.NoError(err) + + tx, err := msgEthereumTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "aphoton") + suite.NoError(err) + txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder() + txBz, err := txEncoder(tx) + suite.NoError(err) + + testCases := []struct { + name string + registerMock func() + tx *evmtypes.MsgEthereumTx + block *types.Block + blockResult *abci.ResponseFinalizeBlock + expTxReceipt map[string]interface{} + expKeyMatchPass bool + expApiPass bool + }{ + { + "success- Receipts with a different chain ID within a patched height", + func() { + + ctx := server.NewDefaultContext() + + // overwrite client conext + suite.backend.clientCtx.WithChainID("canto_7700-1").WithHeight(patchedHeight) + + idxer := indexer.NewKVIndexer(dbm.NewMemDB(), ctx.Logger, suite.backend.clientCtx) + suite.backend = NewBackend(ctx, ctx.Logger, suite.backend.clientCtx, true, idxer) + suite.backend.queryClient.QueryClient = mocks.NewEVMQueryClient(suite.T()) + suite.backend.clientCtx.Client = mocks.NewClient(suite.T()) + suite.backend.queryClient.FeeMarket = mocks.NewFeeMarketQueryClient(suite.T()) + suite.backend.ctx = rpctypes.ContextWithHeight(patchedHeight) + + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterParams(queryClient, &header, patchedHeight) + RegisterParamsWithoutHeader(queryClient, patchedHeight) + RegisterBlock(client, patchedHeight, txBz) + RegisterBlockResults(client, patchedHeight) + }, + msgEthereumTx, + &types.Block{Header: types.Header{Height: patchedHeight}, Data: types.Data{Txs: []types.Tx{txBz}}}, + &abci.ResponseFinalizeBlock{ + TxResults: []*abci.ExecTxResult{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + {Key: "status", Value: "0x0"}, + }}, + }, + }, + }, + }, + map[string]interface{}(nil), + true, + true, + }, + { + "false- Receipts with a different chain ID not in the patched height. expected to return an invalid chain ID", + func() { + + ctx := server.NewDefaultContext() + + // overwrite client conext + suite.backend.clientCtx = suite.backend.clientCtx.WithChainID("canto_7700-1").WithHeight(notPatchedHeight) + + idxer := indexer.NewKVIndexer(dbm.NewMemDB(), ctx.Logger, suite.backend.clientCtx) + suite.backend = NewBackend(ctx, ctx.Logger, suite.backend.clientCtx, true, idxer) + suite.backend.queryClient.QueryClient = mocks.NewEVMQueryClient(suite.T()) + suite.backend.clientCtx.Client = mocks.NewClient(suite.T()) + suite.backend.queryClient.FeeMarket = mocks.NewFeeMarketQueryClient(suite.T()) + suite.backend.ctx = rpctypes.ContextWithHeight(notPatchedHeight) + + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterParams(queryClient, &header, notPatchedHeight) + RegisterParamsWithoutHeader(queryClient, notPatchedHeight) + RegisterBlock(client, notPatchedHeight, txBz) + RegisterBlockResults(client, notPatchedHeight) + }, + msgEthereumTx, + &types.Block{Header: types.Header{Height: notPatchedHeight}, Data: types.Data{Txs: []types.Tx{txBz}}}, + &abci.ResponseFinalizeBlock{ + TxResults: []*abci.ExecTxResult{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + {Key: "status", Value: "0x0"}, + }}, + }, + }, + }, + }, + map[string]interface{}(nil), + false, + false, // invalid chain id error + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + db := dbm.NewMemDB() + suite.backend.indexer = indexer.NewKVIndexer(db, log.NewNopLogger(), suite.backend.clientCtx) + err := suite.backend.indexer.IndexBlock(tc.block, tc.blockResult) + suite.Require().NoError(err) + + txReceipt, err := suite.backend.GetTransactionReceipt(common.HexToHash(tc.tx.Hash)) + if tc.expApiPass { + suite.Require().NoError(err) + + if tc.expKeyMatchPass { + + // check all expected receipt keys are exist. + for k, _ := range tc.expTxReceipt { + _, keyExist := txReceipt[k] + suite.Require().True(keyExist) + } + + } else { + + anyKeyNotExist := false + // check the receipt keys are exist. + for k, _ := range tc.expTxReceipt { + _, keyExist := txReceipt[k] + if !keyExist { + anyKeyNotExist = true + } + + } + suite.Require().True(anyKeyNotExist) + } + } else { + suite.Require().Error(err) + } + + }) + } +} + func (suite *BackendTestSuite) TestGetGasUsed() { origin := suite.backend.cfg.JSONRPC.FixRevertGasRefundHeight testCases := []struct {