From c857eb8e03ffed864d31f8cc216567ac53e1a028 Mon Sep 17 00:00:00 2001 From: Yiming Zang <50607998+yzang2019@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:32:29 -0800 Subject: [PATCH] [EVM] Add new RPC endpoint eth_getBlockReceipts (#1270) * Add new endpoint GetBlockReceipt * Remove prints * Fix endpoint name * Fix comment * Fix unit test * Fix unit test * Fix import * Fix unit test --- evmrpc/block.go | 58 +++++++++++++++++++++++- evmrpc/block_test.go | 19 ++++++++ evmrpc/filter.go | 2 +- evmrpc/filter_test.go | 6 +-- evmrpc/setup_test.go | 101 ++++++++++++++++++++++-------------------- 5 files changed, 132 insertions(+), 54 deletions(-) diff --git a/evmrpc/block.go b/evmrpc/block.go index 89a396d321..b77832a13b 100644 --- a/evmrpc/block.go +++ b/evmrpc/block.go @@ -4,10 +4,10 @@ import ( "context" "errors" "math/big" + "strings" + "sync" "time" - tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -19,6 +19,7 @@ import ( "github.com/sei-protocol/sei-chain/x/evm/types" rpcclient "github.com/tendermint/tendermint/rpc/client" "github.com/tendermint/tendermint/rpc/coretypes" + tmtypes "github.com/tendermint/tendermint/types" ) type BlockAPI struct { @@ -88,6 +89,59 @@ func (a *BlockAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, return EncodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, a.keeper, a.txConfig.TxDecoder(), fullTx) } +func (a *BlockAPI) GetBlockReceipts(ctx context.Context, number rpc.BlockNumber) (result []map[string]interface{}, returnErr error) { + startTime := time.Now() + defer recordMetrics("eth_getBlockReceipts", startTime, returnErr == nil) + // Get height from params + heightPtr, err := getBlockNumber(ctx, a.tmClient, number) + if err != nil { + return nil, err + } + // Get the block by height + block, err := blockWithRetry(ctx, a.tmClient, heightPtr) + if err != nil { + return nil, err + } + // Get all tx hashes for the block + height := LatestCtxHeight + if heightPtr != nil { + height = *heightPtr + } + txHashes := a.keeper.GetTxHashesOnHeight(a.ctxProvider(height), height) + // Get tx receipts for all hashes in parallel + wg := sync.WaitGroup{} + mtx := sync.Mutex{} + allReceipts := make([]map[string]interface{}, len(txHashes)) + for i, hash := range txHashes { + wg.Add(1) + go func(i int, hash common.Hash) { + defer wg.Done() + receipt, err := a.keeper.GetReceipt(a.ctxProvider(height), hash) + if err != nil { + // When the transaction doesn't exist, skip it + if !strings.Contains(err.Error(), "not found") { + mtx.Lock() + returnErr = err + mtx.Unlock() + } + } else { + encodedReceipt, err := encodeReceipt(receipt, block.BlockID.Hash) + if err != nil { + mtx.Lock() + returnErr = err + mtx.Unlock() + } + allReceipts[i] = encodedReceipt + } + }(i, hash) + } + wg.Wait() + if returnErr != nil { + return nil, returnErr + } + return allReceipts, nil +} + func EncodeTmBlock( ctx sdk.Context, block *coretypes.ResultBlock, diff --git a/evmrpc/block_test.go b/evmrpc/block_test.go index e31ef10e53..9ce44814a1 100644 --- a/evmrpc/block_test.go +++ b/evmrpc/block_test.go @@ -49,6 +49,25 @@ func TestGetBlockTransactionCount(t *testing.T) { require.Equal(t, "0x1", resObj["result"]) } +func TestGetBlockReceipts(t *testing.T) { + resObj := sendRequestGood(t, "getBlockReceipts", "0x2") + result := resObj["result"].([]interface{}) + require.Equal(t, 3, len(result)) + receipt1 := result[0].(map[string]interface{}) + require.Equal(t, "0x2", receipt1["blockNumber"]) + require.Equal(t, "0x0", receipt1["transactionIndex"]) + require.Equal(t, "0x0123456789012345678902345678901234567890123456789012345678900001", receipt1["transactionHash"]) + receipt2 := result[1].(map[string]interface{}) + require.Equal(t, "0x2", receipt2["blockNumber"]) + require.Equal(t, "0x1", receipt2["transactionIndex"]) + require.Equal(t, "0x0123456789012345678902345678901234567890123456789012345678900002", receipt2["transactionHash"]) + receipt3 := result[2].(map[string]interface{}) + require.Equal(t, "0x2", receipt3["blockNumber"]) + require.Equal(t, "0x2", receipt3["transactionIndex"]) + require.Equal(t, "0x0123456789012345678902345678901234567890123456789012345678900003", receipt3["transactionHash"]) + +} + func verifyBlockResult(t *testing.T, resObj map[string]interface{}) { resObj = resObj["result"].(map[string]interface{}) require.Equal(t, "0x0", resObj["difficulty"]) diff --git a/evmrpc/filter.go b/evmrpc/filter.go index ed4a919f3f..65a729b440 100644 --- a/evmrpc/filter.go +++ b/evmrpc/filter.go @@ -334,7 +334,7 @@ func (f *LogFetcher) FindLogsByBloom(height int64, filters [][]bloomIndexes) (re ctx.Logger().Error(fmt.Sprintf("FindLogsByBloom: unable to find receipt for hash %s", hash.Hex())) continue } - if MatchFilters(ethtypes.Bloom(receipt.LogsBloom), filters) { + if len(receipt.LogsBloom) > 0 && MatchFilters(ethtypes.Bloom(receipt.LogsBloom), filters) { res = append(res, keeper.GetLogsForTx(receipt)...) } } diff --git a/evmrpc/filter_test.go b/evmrpc/filter_test.go index 942461f76c..3a6cae2217 100644 --- a/evmrpc/filter_test.go +++ b/evmrpc/filter_test.go @@ -134,8 +134,8 @@ func TestFilterGetLogs(t *testing.T) { common.HexToAddress("0x1111111111111111111111111111111111111113"), }, topics: [][]common.Hash{ - {common.Hash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000123"))}, - {common.Hash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000456"))}, + {common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000123")}, + {common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000456")}, }, wantErr: false, check: func(t *testing.T, log map[string]interface{}) { @@ -155,7 +155,7 @@ func TestFilterGetLogs(t *testing.T) { toBlock: "0x2", topics: [][]common.Hash{ {}, - {common.Hash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000456"))}, + {common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000456")}, }, wantErr: false, check: func(t *testing.T, log map[string]interface{}) { diff --git a/evmrpc/setup_test.go b/evmrpc/setup_test.go index a896bf3d99..b6f1d0ee2a 100644 --- a/evmrpc/setup_test.go +++ b/evmrpc/setup_test.go @@ -15,6 +15,7 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/crypto/hd" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" @@ -337,6 +338,7 @@ func init() { panic(err) } testApp.Commit(context.Background()) + // Start good http server goodConfig := evmrpc.DefaultConfig goodConfig.HTTPPort = TestPort goodConfig.WSPort = TestWSPort @@ -352,6 +354,8 @@ func init() { if err := HttpServer.Start(); err != nil { panic(err) } + + // Start bad http server badConfig := evmrpc.DefaultConfig badConfig.HTTPPort = TestBadPort badConfig.FilterTimeout = 500 * time.Millisecond @@ -362,6 +366,8 @@ func init() { if err := badHTTPServer.Start(); err != nil { panic(err) } + + // Start ws server wsServer, err := evmrpc.NewEVMWebSocketServer(infoLog, goodConfig, &MockClient{}, EVMKeeper, func(int64) sdk.Context { return Ctx }, TxConfig, "") if err != nil { panic(err) @@ -372,9 +378,17 @@ func init() { fmt.Printf("wsServer started with config = %+v\n", goodConfig) time.Sleep(1 * time.Second) + // Generate data + generateTxData() + + // Setup logs + setupLogs() +} + +func generateTxData() { chainId := big.NewInt(types.DefaultChainID.Int64()) to := common.HexToAddress("010203") - txData := ethtypes.DynamicFeeTx{ + txBuilder, tx := buildTx(ethtypes.DynamicFeeTx{ Nonce: 1, GasFeeCap: big.NewInt(10), Gas: 1000, @@ -382,33 +396,8 @@ func init() { Value: big.NewInt(1000), Data: []byte("abc"), ChainID: chainId, - } - mnemonic := "fish mention unlock february marble dove vintage sand hub ordinary fade found inject room embark supply fabric improve spike stem give current similar glimpse" - derivedPriv, _ := hd.Secp256k1.Derive()(mnemonic, "", "") - privKey := hd.Secp256k1.Generate()(derivedPriv) - testPrivHex := hex.EncodeToString(privKey.Bytes()) - key, _ := crypto.HexToECDSA(testPrivHex) - evmParams := EVMKeeper.GetParams(Ctx) - ethCfg := evmParams.GetChainConfig().EthereumConfig(chainId) - signer := ethtypes.MakeSigner(ethCfg, big.NewInt(Ctx.BlockHeight()), uint64(Ctx.BlockTime().Unix())) - tx := ethtypes.NewTx(&txData) - tx, err = ethtypes.SignTx(tx, signer, key) - if err != nil { - panic(err) - } - typedTx, err := ethtx.NewDynamicFeeTx(tx) - if err != nil { - panic(err) - } - msg, err := types.NewMsgEVMTransaction(typedTx) - if err != nil { - panic(err) - } - b := TxConfig.NewTxBuilder() - if err := b.SetMsgs(msg); err != nil { - panic(err) - } - Tx = b.GetTx() + }) + Tx = txBuilder.GetTx() TxNonEvm = app.TestTx{} if err := EVMKeeper.SetReceipt(Ctx, tx.Hash(), &types.Receipt{ From: "0x1234567890123456789012345678901234567890", @@ -429,7 +418,7 @@ func init() { }); err != nil { panic(err) } - seiAddr, err = sdk.AccAddressFromHex(common.Bytes2Hex([]byte("seiAddr"))) + seiAddr, err := sdk.AccAddressFromHex(common.Bytes2Hex([]byte("seiAddr"))) if err != nil { panic(err) } @@ -452,8 +441,7 @@ func init() { common.HexToAddress("0x1df809C639027b465B931BD63Ce71c8E5834D9d6"), ) EVMKeeper.SetNonce(Ctx, common.HexToAddress("0x1234567890123456789012345678901234567890"), 1) - - unconfirmedTxData := ethtypes.DynamicFeeTx{ + unconfirmedTxBuilder, _ := buildTx(ethtypes.DynamicFeeTx{ Nonce: 2, GasFeeCap: big.NewInt(10), Gas: 1000, @@ -461,27 +449,39 @@ func init() { Value: big.NewInt(2000), Data: []byte("abc"), ChainID: chainId, - } - tx = ethtypes.NewTx(&unconfirmedTxData) - tx, err = ethtypes.SignTx(tx, signer, key) + }) + UnconfirmedTx = unconfirmedTxBuilder.GetTx() +} + +func buildTx(txData ethtypes.DynamicFeeTx) (client.TxBuilder, *ethtypes.Transaction) { + chainId := big.NewInt(types.DefaultChainID.Int64()) + mnemonic := "fish mention unlock february marble dove vintage sand hub ordinary fade found inject room embark supply fabric improve spike stem give current similar glimpse" + derivedPriv, _ := hd.Secp256k1.Derive()(mnemonic, "", "") + privKey := hd.Secp256k1.Generate()(derivedPriv) + testPrivHex := hex.EncodeToString(privKey.Bytes()) + key, _ := crypto.HexToECDSA(testPrivHex) + evmParams := EVMKeeper.GetParams(Ctx) + ethCfg := evmParams.GetChainConfig().EthereumConfig(chainId) + signer := ethtypes.MakeSigner(ethCfg, big.NewInt(Ctx.BlockHeight()), uint64(Ctx.BlockTime().Unix())) + tx := ethtypes.NewTx(&txData) + tx, err := ethtypes.SignTx(tx, signer, key) if err != nil { panic(err) } - typedTx, err = ethtx.NewDynamicFeeTx(tx) + + typedTx, err := ethtx.NewDynamicFeeTx(tx) if err != nil { panic(err) } - msg, err = types.NewMsgEVMTransaction(typedTx) + msg, err := types.NewMsgEVMTransaction(typedTx) if err != nil { panic(err) } - b = TxConfig.NewTxBuilder() - if err := b.SetMsgs(msg); err != nil { + builder := TxConfig.NewTxBuilder() + if err := builder.SetMsgs(msg); err != nil { panic(err) } - UnconfirmedTx = b.GetTx() - - setupLogs() + return builder, tx } func setupLogs() { @@ -499,8 +499,10 @@ func setupLogs() { }, }}}}) EVMKeeper.SetReceipt(Ctx, common.HexToHash("0x123456789012345678902345678901234567890123456789012345678900001"), &types.Receipt{ - BlockNumber: 2, - LogsBloom: bloom1[:], + BlockNumber: 2, + TransactionIndex: 0, + TxHashHex: "0x123456789012345678902345678901234567890123456789012345678900001", + LogsBloom: bloom1[:], Logs: []*types.Log{{ Address: "0x1111111111111111111111111111111111111112", Topics: []string{"0x0000000000000000000000000000000000000000000000000000000000000123", "0x0000000000000000000000000000000000000000000000000000000000000456"}, @@ -517,8 +519,10 @@ func setupLogs() { }, }}}}) EVMKeeper.SetReceipt(Ctx, common.HexToHash("0x123456789012345678902345678901234567890123456789012345678900002"), &types.Receipt{ - BlockNumber: 2, - LogsBloom: bloom2[:], + BlockNumber: 2, + TransactionIndex: 1, + TxHashHex: "0x123456789012345678902345678901234567890123456789012345678900002", + LogsBloom: bloom2[:], Logs: []*types.Log{{ Address: "0x1111111111111111111111111111111111111113", Topics: []string{"0x0000000000000000000000000000000000000000000000000000000000000123", "0x0000000000000000000000000000000000000000000000000000000000000456"}, @@ -532,8 +536,10 @@ func setupLogs() { }, }}}}) EVMKeeper.SetReceipt(Ctx, common.HexToHash("0x123456789012345678902345678901234567890123456789012345678900003"), &types.Receipt{ - BlockNumber: 2, - LogsBloom: bloom3[:], + BlockNumber: 2, + TransactionIndex: 2, + TxHashHex: "0x123456789012345678902345678901234567890123456789012345678900003", + LogsBloom: bloom3[:], Logs: []*types.Log{{ Address: "0x1111111111111111111111111111111111111114", Topics: []string{"0x0000000000000000000000000000000000000000000000000000000000000123", "0x0000000000000000000000000000000000000000000000000000000000000456"}, @@ -545,7 +551,6 @@ func setupLogs() { common.HexToHash("0x123456789012345678902345678901234567890123456789012345678900003"), }) EVMKeeper.SetBlockBloom(Ctx, 2, []ethtypes.Bloom{bloom1, bloom2, bloom3}) - } //nolint:deadcode