diff --git a/CHANGELOG.md b/CHANGELOG.md index 898b5f5800..0af582df90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - (lint) [#83](https://github.com/EscanBE/evermint/pull/83) Update lint rules and fix lint issues - (cleanup) [#92](https://github.com/EscanBE/evermint/pull/92) Cleanup un-used `x/evm` and `x/erc20` types - (indexer) [#96](https://github.com/EscanBE/evermint/pull/96) Make EVMTxIndexer mandatory service, starts before Json-RPC +- (test) [#100](https://github.com/EscanBE/evermint/pull/100) Add some edge test cases + benchmark tests ### Bug Fixes diff --git a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_account_test.go b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_account_test.go index f481eff8e3..ce606026f9 100644 --- a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_account_test.go +++ b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_account_test.go @@ -1,4 +1,4 @@ -package demo +package eth_rpc_it_suite import ( "context" diff --git a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_benchmark_test.go b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_benchmark_test.go new file mode 100644 index 0000000000..7c0ada69eb --- /dev/null +++ b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_block_benchmark_test.go @@ -0,0 +1,111 @@ +package eth_rpc_it_suite + +import ( + rpctypes "github.com/EscanBE/evermint/v12/rpc/types" + "testing" +) + +func genericSetupTestSuiteForBenchmarkGetBlock() (suite *EthRpcTestSuite, rpcTx *rpctypes.RPCTransaction, cleanup func()) { + suite = new(EthRpcTestSuite) + suite.SetT(&testing.T{}) + suite.SetupSuite() + suite.SetupTest() + + deployer := suite.CITS.WalletAccounts.Number(1) + + _, msgEthTx, _, err := suite.CITS.TxDeploy4Nft1155Contract(deployer, deployer) // deployment of this contract emits some evm-events + suite.Require().NoError(err) + suite.Require().NotNil(msgEthTx) + + suite.Commit() // commit to passive trigger EVM Tx indexer + + rpcTx, err = suite.GetEthPublicAPI().GetTransactionByHash(msgEthTx.AsTransaction().Hash()) + suite.Require().NoError(err) + suite.Require().NotNil(rpcTx) + suite.Require().NotNil(rpcTx.BlockHash) + + return suite, rpcTx, func() { + suite.TearDownTest() + suite.TearDownSuite() + } +} + +func BenchmarkGetBlockByNumber(b *testing.B) { + // 2024 Jan 17th: 6.787m ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetBlock() + defer cleanup() + + blockNumber := rpctypes.BlockNumber(rpcTx.BlockNumber.ToInt().Int64()) + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + blk, err := ethPublicAPI.GetBlockByNumber(blockNumber, true) + b.StopTimer() + suite.Require().NoError(err) + suite.Require().NotNil(blk) + } +} + +func BenchmarkGetBlockByHash(b *testing.B) { + // 2024 Jan 17th: 6.393m ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetBlock() + defer cleanup() + + blockHash := *rpcTx.BlockHash + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + blk, err := ethPublicAPI.GetBlockByHash(blockHash, true) + b.StopTimer() + suite.Require().NoError(err) + suite.Require().NotNil(blk) + } +} + +func BenchmarkGetBlockTransactionCountByNumber(b *testing.B) { + // 2024 Jan 17th: 5.534m ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetBlock() + defer cleanup() + + blockNumber := rpctypes.BlockNumber(rpcTx.BlockNumber.ToInt().Int64()) + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + cnt := ethPublicAPI.GetBlockTransactionCountByNumber(blockNumber) + b.StopTimer() + suite.Require().NotNil(cnt) + suite.Require().NotZero(uint(*cnt)) + } +} + +func BenchmarkGetBlockTransactionCountByHash(b *testing.B) { + // 2024 Jan 17th: 5.623m ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetBlock() + defer cleanup() + + blockHash := *rpcTx.BlockHash + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + cnt := ethPublicAPI.GetBlockTransactionCountByHash(blockHash) + b.StopTimer() + suite.Require().NotNil(cnt) + suite.Require().NotZero(uint(*cnt)) + } +} 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 c5e0bb53c7..04399ce540 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 @@ -1,4 +1,4 @@ -package demo +package eth_rpc_it_suite import ( "context" diff --git a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_benchmark_test.go b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_benchmark_test.go new file mode 100644 index 0000000000..5c4699fe50 --- /dev/null +++ b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/eth_api_transaction_benchmark_test.go @@ -0,0 +1,131 @@ +package eth_rpc_it_suite + +import ( + rpctypes "github.com/EscanBE/evermint/v12/rpc/types" + "testing" +) + +func genericSetupTestSuiteForBenchmarkGetTransaction() (suite *EthRpcTestSuite, rpcTx *rpctypes.RPCTransaction, cleanup func()) { + suite = new(EthRpcTestSuite) + suite.SetT(&testing.T{}) + suite.SetupSuite() + suite.SetupTest() + + deployer := suite.CITS.WalletAccounts.Number(1) + + _, msgEthTx, _, err := suite.CITS.TxDeploy4Nft1155Contract(deployer, deployer) // deployment of this contract emits some evm-events + suite.Require().NoError(err) + suite.Require().NotNil(msgEthTx) + + suite.Commit() // commit to passive trigger EVM Tx indexer + + rpcTx, err = suite.GetEthPublicAPI().GetTransactionByHash(msgEthTx.AsTransaction().Hash()) + suite.Require().NoError(err) + suite.Require().NotNil(rpcTx) + suite.Require().NotNil(rpcTx.BlockHash) + + return suite, rpcTx, func() { + suite.TearDownTest() + suite.TearDownSuite() + } +} + +func BenchmarkGetTransactionByHash(b *testing.B) { + // 2024 Jan 17th: 5.776 ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetTransaction() + defer cleanup() + + txHash := rpcTx.Hash + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + tx, err := ethPublicAPI.GetTransactionByHash(txHash) + b.StopTimer() + suite.Require().NoError(err) + suite.Require().NotNil(tx) + } +} + +func BenchmarkGetTransactionReceipt(b *testing.B) { + // 2024 Jan 17th: 6.152m ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetTransaction() + defer cleanup() + + txHash := rpcTx.Hash + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + tx, err := ethPublicAPI.GetTransactionReceipt(txHash) + b.StopTimer() + suite.Require().NoError(err) + suite.Require().NotNil(tx) + } +} + +func BenchmarkGetTransactionLogs(b *testing.B) { + // 2024 Jan 17th: 3.874m ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetTransaction() + defer cleanup() + + txHash := rpcTx.Hash + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + tx, err := ethPublicAPI.GetTransactionLogs(txHash) + b.StopTimer() + suite.Require().NoError(err) + suite.Require().NotNil(tx) + } +} + +func BenchmarkGetTransactionByBlockNumberAndIndex(b *testing.B) { + // 2024 Jan 17th: 6.103m ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetTransaction() + defer cleanup() + + blockNumber := rpctypes.BlockNumber(rpcTx.BlockNumber.ToInt().Int64()) + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + tx, err := ethPublicAPI.GetTransactionByBlockNumberAndIndex(blockNumber, 0) + b.StopTimer() + suite.Require().NoError(err) + suite.Require().NotNil(tx) + } +} + +func BenchmarkGetTransactionByBlockHashAndIndex(b *testing.B) { + // 2024 Jan 17th: 6.067m ns/op + + suite, rpcTx, cleanup := genericSetupTestSuiteForBenchmarkGetTransaction() + defer cleanup() + + blockHash := *rpcTx.BlockHash + + ethPublicAPI := suite.GetEthPublicAPI() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + tx, err := ethPublicAPI.GetTransactionByBlockHashAndIndex(blockHash, 0) + b.StopTimer() + suite.Require().NoError(err) + suite.Require().NotNil(tx) + } +} 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 c4167bccff..255d9affb2 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 @@ -1,4 +1,4 @@ -package demo +package eth_rpc_it_suite import ( "encoding/hex" @@ -637,6 +637,112 @@ func (suite *EthRpcTestSuite) Test_GetTransactionByBlockNumberAndHashAndIndex() fetchAndCompareWithGetTransactionByHash(rpcTx) }) + + suite.Run("get tx by index not found", func() { + deployer := suite.CITS.WalletAccounts.Number(1) + + _, sentEvmTx1, _, err := suite.CITS.TxDeploy1StorageContract(deployer) + suite.Require().NoError(err) + + suite.CITS.Commit() // commit to passive trigger EVM Tx indexer + + // shift some blocks + for i := 0; i < int(rand.Uint32()%5)+2; i++ { + suite.CITS.Commit() + } + + _, sentEvmTx2, _, err := suite.CITS.TxDeploy1StorageContract(deployer) + suite.Require().NoError(err) + + suite.CITS.Commit() // commit to passive trigger EVM Tx indexer + + { + // shift two more blocks + suite.CITS.Commit() + suite.CITS.Commit() + } + + blockHeightWithoutTxs := suite.CITS.GetLatestBlockHeight() - 1 + blockWithoutTxs, err := suite.GetEthPublicAPI().GetBlockByNumber(rpctypes.BlockNumber(blockHeightWithoutTxs), false) + suite.Require().NoError(err) + suite.Require().NotNil(blockWithoutTxs) + blockHashOfBlockWithoutTxs := common.BytesToHash(blockWithoutTxs["hash"].(hexutil.Bytes)) + + // verifies that txs are successfully indexed + sentTxHash1 := sentEvmTx1.AsTransaction().Hash() + rpcTx1, err := suite.GetEthPublicAPI().GetTransactionByHash(sentTxHash1) + suite.Require().NoError(err) + suite.Require().NotNil(rpcTx1) + suite.Require().NotNil(rpcTx1.BlockHash) + suite.Equal(sentTxHash1, rpcTx1.Hash) + + sentTxHash2 := sentEvmTx2.AsTransaction().Hash() + rpcTx2, err := suite.GetEthPublicAPI().GetTransactionByHash(sentTxHash2) + suite.Require().NoError(err) + suite.Require().NotNil(rpcTx2) + suite.Require().NotNil(rpcTx2.BlockHash) + suite.Equal(sentTxHash2, rpcTx2.Hash) + + suite.Require().NotEqual(rpcTx1.BlockHash.Hex(), rpcTx2.BlockHash.Hex(), "txs must be processed in different blocks") + + assertValidResult := func(sourceRpcTx, gotRpcTx *rpctypes.RPCTransaction) { + suite.True(reflect.DeepEqual(sourceRpcTx, gotRpcTx)) + } + + // verifies that correct query will return correct result + + // GetTransactionByBlockNumberAndIndex + testRpcTx1, err := suite.GetEthPublicAPI().GetTransactionByBlockNumberAndIndex(rpctypes.BlockNumber(rpcTx1.BlockNumber.ToInt().Int64()), hexutil.Uint(0)) + suite.Require().NoError(err) + assertValidResult(rpcTx1, testRpcTx1) + + testRpcTx2, err := suite.GetEthPublicAPI().GetTransactionByBlockNumberAndIndex(rpctypes.BlockNumber(rpcTx2.BlockNumber.ToInt().Int64()), hexutil.Uint(0)) + suite.Require().NoError(err) + assertValidResult(rpcTx2, testRpcTx2) + + //GetTransactionByBlockHashAndIndex + testRpcTx1, err = suite.GetEthPublicAPI().GetTransactionByBlockHashAndIndex(*rpcTx1.BlockHash, hexutil.Uint(0)) + suite.Require().NoError(err) + assertValidResult(rpcTx1, testRpcTx1) + + testRpcTx2, err = suite.GetEthPublicAPI().GetTransactionByBlockHashAndIndex(*rpcTx2.BlockHash, hexutil.Uint(0)) + suite.Require().NoError(err) + assertValidResult(rpcTx2, testRpcTx2) + + // verifies that incorrect query will return nil result + + // Out of bound index + + // GetTransactionByBlockNumberAndIndex + testRpcTx1QueryByOutOfBoundIndex, err := suite.GetEthPublicAPI().GetTransactionByBlockNumberAndIndex(rpctypes.BlockNumber(rpcTx1.BlockNumber.ToInt().Int64()), hexutil.Uint(1)) + suite.Require().NoError(err) + suite.Nil(testRpcTx1QueryByOutOfBoundIndex) + + testRpcTx2QueryByOutOfBoundIndex, err := suite.GetEthPublicAPI().GetTransactionByBlockNumberAndIndex(rpctypes.BlockNumber(rpcTx2.BlockNumber.ToInt().Int64()), hexutil.Uint(1)) + suite.Require().NoError(err) + suite.Nil(testRpcTx2QueryByOutOfBoundIndex) + + //GetTransactionByBlockHashAndIndex + testRpcTx1QueryByOutOfBoundIndex, err = suite.GetEthPublicAPI().GetTransactionByBlockHashAndIndex(*rpcTx1.BlockHash, hexutil.Uint(1)) + suite.Require().NoError(err) + suite.Nil(testRpcTx1QueryByOutOfBoundIndex) + + testRpcTx2QueryByOutOfBoundIndex, err = suite.GetEthPublicAPI().GetTransactionByBlockHashAndIndex(*rpcTx2.BlockHash, hexutil.Uint(1)) + suite.Require().NoError(err) + suite.Nil(testRpcTx2QueryByOutOfBoundIndex) + + // Not correct block number & hash + + // GetTransactionByBlockNumberAndIndex + testRpcTxQueryByNotCorrectBlockNumber, err := suite.GetEthPublicAPI().GetTransactionByBlockNumberAndIndex(rpctypes.BlockNumber(blockHeightWithoutTxs), hexutil.Uint(0)) + suite.Require().NoError(err) + suite.Nil(testRpcTxQueryByNotCorrectBlockNumber) + + //GetTransactionByBlockHashAndIndex + testRpcTxQueryByNotCorrectBlockHash, err := suite.GetEthPublicAPI().GetTransactionByBlockHashAndIndex(blockHashOfBlockWithoutTxs, hexutil.Uint(0)) + suite.Require().NoError(err) + suite.Nil(testRpcTxQueryByNotCorrectBlockHash) + }) } func (suite *EthRpcTestSuite) Test_SendRawTransaction() { diff --git a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/setup_eth_rpc_integration_test.go b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/setup_eth_rpc_integration_test.go index a2793f4fe9..d8637ee817 100644 --- a/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/setup_eth_rpc_integration_test.go +++ b/rpc/namespaces/ethereum/eth/eth_rpc_it_suite/setup_eth_rpc_integration_test.go @@ -1,4 +1,4 @@ -package demo +package eth_rpc_it_suite //goland:noinspection SpellCheckingInspection import ( diff --git a/server/indexer_service.go b/server/indexer_service.go index 7503e23161..526c08446d 100644 --- a/server/indexer_service.go +++ b/server/indexer_service.go @@ -82,7 +82,8 @@ func (eis *EVMIndexerService) OnStart() error { lastIndexedBlock = latestBlock } else if lastIndexedBlock < status.SyncInfo.EarliestBlockHeight { lastIndexedBlock = status.SyncInfo.EarliestBlockHeight - // kinda unsafe, but we don't have a better way to do this + // Kinda unsafe, but we don't have a better way to do this. + // In-case `EarliestBlockHeight` is zero one some nodes, it will be handled by the failure tracker with threshold. } var isIndexerMarkedReady bool @@ -92,7 +93,7 @@ func (eis *EVMIndexerService) OnStart() error { if cnt, found := startupIndexBlockFailureTracker[h]; found { cnt++ startupIndexBlockFailureTracker[h] = cnt - if cnt >= startupIndexBlockFailureThreshold { + if cnt > startupIndexBlockFailureThreshold { shouldSkip = true } } else {