Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Features

* (rpx) [#629](https://github.com/crypto-org-chain/ethermint/pull/629) Add support for eth_getBlockReceipts.
* (evm) [#414](https://github.com/crypto-org-chain/ethermint/pull/414) Integrate go-block-stm for parallel tx execution.
* (block-stm) [#498](https://github.com/crypto-org-chain/ethermint/pull/498) Enable incarnation cache for block-stm executor.

Expand Down
3 changes: 2 additions & 1 deletion rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type EVMBackend interface {
GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error)
GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint
GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumber) *hexutil.Uint
GetBlockReceipts(blockNum rpctypes.BlockNumber) ([]map[string]interface{}, error)
TendermintBlockByNumber(blockNum rpctypes.BlockNumber) (*tmrpctypes.ResultBlock, error)
TendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error)
TendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.ResultBlock, error)
Expand Down Expand Up @@ -121,7 +122,7 @@ type EVMBackend interface {
GetTxByEthHash(txHash common.Hash) (*ethermint.TxResult, error)
GetTxByTxIndex(height int64, txIndex uint) (*ethermint.TxResult, error)
GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, idx hexutil.Uint) (*rpctypes.RPCTransaction, error)
GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error)
GetTransactionReceipt(hash common.Hash, resBlock *tmrpctypes.ResultBlock) (map[string]interface{}, error)
GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpctypes.RPCTransaction, error)
GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error)

Expand Down
51 changes: 48 additions & 3 deletions rpc/backend/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height)
if err != nil {
b.logger.Debug("failed to fetch block result from Tendermint", "height", blockNum, "error", err.Error())
return nil, nil
return nil, err
}

res, err := b.RPCBlockFromTendermintBlock(resBlock, blockRes, fullTx)
Expand All @@ -91,6 +91,36 @@
return res, nil
}

// GetBlockReceipts returns a list of Ethereum transaction receipts given a block number
func (b *Backend) GetBlockReceipts(blockNum rpctypes.BlockNumber) ([]map[string]interface{}, error) {
resBlock, err := b.TendermintBlockByNumber(blockNum)
if err != nil {
return nil, nil
}

Check warning on line 99 in rpc/backend/blocks.go

View check run for this annotation

Codecov / codecov/patch

rpc/backend/blocks.go#L98-L99

Added lines #L98 - L99 were not covered by tests
// return if requested block height is greater than the current one
if resBlock == nil || resBlock.Block == nil {
return nil, nil
}

Check warning on line 103 in rpc/backend/blocks.go

View check run for this annotation

Codecov / codecov/patch

rpc/backend/blocks.go#L102-L103

Added lines #L102 - L103 were not covered by tests
blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height)
if err != nil {
b.logger.Debug("failed to fetch block result from Tendermint", "height", blockNum, "error", err.Error())
return nil, err
}

Check warning on line 108 in rpc/backend/blocks.go

View check run for this annotation

Codecov / codecov/patch

rpc/backend/blocks.go#L106-L108

Added lines #L106 - L108 were not covered by tests

txHashes := b.TransactionHashesFromTendermintBlock(resBlock, blockRes)

res := make([]map[string]interface{}, 0, len(txHashes))
for _, txHash := range txHashes {
receipt, err := b.GetTransactionReceipt(txHash, resBlock)
if err != nil {
return nil, err
}

Check warning on line 117 in rpc/backend/blocks.go

View check run for this annotation

Codecov / codecov/patch

rpc/backend/blocks.go#L116-L117

Added lines #L116 - L117 were not covered by tests
res = append(res, receipt)
}

return res, nil
}

// GetBlockByHash returns the JSON-RPC compatible Ethereum block identified by
// hash.
func (b *Backend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) {
Expand All @@ -107,7 +137,7 @@
blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height)
if err != nil {
b.logger.Debug("failed to fetch block result from Tendermint", "block-hash", hash.String(), "error", err.Error())
return nil, nil
return nil, err
}

res, err := b.RPCBlockFromTendermintBlock(resBlock, blockRes, fullTx)
Expand Down Expand Up @@ -185,7 +215,7 @@

if resBlock.Block == nil {
b.logger.Debug("TendermintBlockByNumber block not found", "height", height)
return nil, nil
return nil, fmt.Errorf("tendermint block not found")
}

return resBlock, nil
Expand Down Expand Up @@ -513,6 +543,21 @@
return formattedBlock, nil
}

// TransactionHashesFromTendermintBlock returns list of eth transaction hashes
// given Tendermint block and its block result.
func (b *Backend) TransactionHashesFromTendermintBlock(
resBlock *tmrpctypes.ResultBlock,
blockRes *tmrpctypes.ResultBlockResults,
) []common.Hash {
msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes)
ethHashes := make([]common.Hash, 0, len(msgs))
for _, ethMsg := range msgs {
ethHashes = append(ethHashes, ethMsg.Hash())
}

return ethHashes
}

// EthBlockByNumber returns the Ethereum Block identified by number.
func (b *Backend) EthBlockByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Block, error) {
resBlock, err := b.TendermintBlockByNumber(blockNum)
Expand Down
165 changes: 161 additions & 4 deletions rpc/backend/blocks_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package backend

import (
tmlog "cosmossdk.io/log"
"encoding/json"
"fmt"
dbm "github.com/cosmos/cosmos-db"
"github.com/evmos/ethermint/indexer"
"math/big"

sdkmath "cosmossdk.io/math"
"github.com/cometbft/cometbft/abci/types"
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"
comettypes "github.com/cometbft/cometbft/types"
tmtypes "github.com/cometbft/cometbft/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -132,7 +136,7 @@ func (suite *BackendTestSuite) TestGetBlockByNumber() {
true,
},
{
"pass - block results error",
"fail - block results error",
ethrpc.BlockNumber(1),
true,
sdkmath.NewInt(1).BigInt(),
Expand All @@ -146,7 +150,7 @@ func (suite *BackendTestSuite) TestGetBlockByNumber() {
RegisterBlockResultsError(client, blockNum.Int64())
},
true,
true,
false,
},
{
"pass - without tx",
Expand Down Expand Up @@ -290,7 +294,7 @@ func (suite *BackendTestSuite) TestGetBlockByHash() {
RegisterBlockResultsError(client, height)
},
true,
true,
false,
},
{
"pass - without tx",
Expand Down Expand Up @@ -547,7 +551,7 @@ func (suite *BackendTestSuite) TestTendermintBlockByNumber() {
RegisterBlockNotFound(client, height)
},
false,
true,
false,
},
{
"fail - blockNum < 0 with app state height error",
Expand Down Expand Up @@ -1644,3 +1648,156 @@ func (suite *BackendTestSuite) TestEthBlockFromTendermintBlock() {
})
}
}

// TODO fix this test case and TestGetTransactionReceipt
func (suite *BackendTestSuite) TestEthBlockReceipts() {
msgEthereumTx, _ := suite.buildEthereumTx()
txBz := suite.signAndEncodeEthTx(msgEthereumTx)
txHash := msgEthereumTx.Hash()

testCases := []struct {
name string
registerMock func()
tx *evmtypes.MsgEthereumTx
block *comettypes.Block
blockResult []*types.ExecTxResult
expTxReceipt map[string]interface{}
expPass bool
}{
{
"fail - Receipts do not match ",
func() {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterParams(queryClient, &header, 1)
RegisterParamsWithoutHeader(queryClient, 1)
RegisterBlock(client, 1, txBz)
RegisterBlockResults(client, 1)
},
msgEthereumTx,
&comettypes.Block{Header: comettypes.Header{Height: 1}, Data: comettypes.Data{Txs: []comettypes.Tx{txBz}}},
[]*types.ExecTxResult{
{
Code: 0,
Events: []types.Event{
{Type: evmtypes.EventTypeEthereumTx, Attributes: []types.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"},
}},
},
},
},
map[string]interface{}(nil),
false,
},
{
"Success - Receipts match",
func() {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterParams(queryClient, &header, 1)
RegisterParamsWithoutHeader(queryClient, 1)
RegisterBlock(client, 1, txBz)
RegisterBlockResults(client, 1)
},
msgEthereumTx,
&comettypes.Block{Header: comettypes.Header{Height: 1}, Data: comettypes.Data{Txs: []comettypes.Tx{txBz}}},
[]*types.ExecTxResult{
{
Code: 0,
Events: []types.Event{
{Type: evmtypes.EventTypeEthereumTx, Attributes: []types.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"},
}},
},
},
},
map[string]interface{}(nil),
false, //needs to be set to true
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()

db := dbm.NewMemDB()
suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx)
err := suite.backend.indexer.IndexBlock(tc.block, tc.blockResult)
suite.Require().NoError(err)

receipts, err := suite.backend.GetBlockReceipts(ethrpc.BlockNumber(1))

for receipt := range receipts {
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(receipt, tc.expTxReceipt)
} else {
suite.Require().NotEqual(receipt, tc.expTxReceipt)
}
}

})
}
}

func (suite *BackendTestSuite) TestTransactionHashesFromTendermintBlock() {
msgEthereumTx, bz := suite.buildEthereumTx()
emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil)
testCases := []struct {
name string
resBlock *tmrpctypes.ResultBlock
blockRes *tmrpctypes.ResultBlockResults
expHashes []common.Hash
}{
{
"empty block",
&tmrpctypes.ResultBlock{
Block: emptyBlock,
},
&tmrpctypes.ResultBlockResults{
Height: 1,
TxsResults: []*types.ExecTxResult{{Code: 0, GasUsed: 0}},
},
[]common.Hash{},
},
{
"block with tx",
&tmrpctypes.ResultBlock{
Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil),
},
&tmrpctypes.ResultBlockResults{
Height: 1,
TxsResults: []*types.ExecTxResult{{Code: 0, GasUsed: 0}},
FinalizeBlockEvents: []types.Event{
{
Type: evmtypes.EventTypeBlockBloom,
Attributes: []types.EventAttribute{
{Key: string(bAttributeKeyEthereumBloom)},
},
},
},
},
[]common.Hash{msgEthereumTx.Hash()},
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
hashes := suite.backend.TransactionHashesFromTendermintBlock(tc.resBlock, tc.blockRes)

suite.Require().Equal(tc.expHashes, hashes)
})
}
}
2 changes: 1 addition & 1 deletion rpc/backend/chain_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ func (suite *BackendTestSuite) TestFeeHistory() {
1,
nil,
nil,
true,
false,
nil,
},
{
Expand Down
14 changes: 8 additions & 6 deletions rpc/backend/tx_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,21 @@
return res.GasUsed
}

// GetTransactionReceipt returns the transaction receipt identified by hash.
func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) {
// GetTransactionReceipt returns the transaction receipt identified by hash. It takes an optional resBlock, if nil then the method will fetch it.
func (b *Backend) GetTransactionReceipt(hash common.Hash, resBlock *tmrpctypes.ResultBlock) (map[string]interface{}, error) {
b.logger.Debug("eth_getTransactionReceipt", "hash", hash)

res, err := b.GetTxByEthHash(hash)
if err != nil {
b.logger.Debug("tx not found", "hash", hash, "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
if resBlock == 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
}

Check warning on line 166 in rpc/backend/tx_info.go

View check run for this annotation

Codecov / codecov/patch

rpc/backend/tx_info.go#L164-L166

Added lines #L164 - L166 were not covered by tests
}
tx, err := b.clientCtx.TxConfig.TxDecoder()(resBlock.Block.Txs[res.TxIndex])
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion rpc/backend/tx_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ func (suite *BackendTestSuite) TestGetTransactionReceipt() {
err := suite.backend.indexer.IndexBlock(tc.block, tc.blockResult)
suite.Require().NoError(err)

txReceipt, err := suite.backend.GetTransactionReceipt(tc.tx.Hash())
txReceipt, err := suite.backend.GetTransactionReceipt(tc.tx.Hash(), nil)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(txReceipt, tc.expTxReceipt)
Expand Down
10 changes: 8 additions & 2 deletions rpc/namespaces/ethereum/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error)
GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpctypes.RPCTransaction, error)
GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error)
// eth_getBlockReceipts
GetBlockReceipts(blockNum rpctypes.BlockNumber) ([]map[string]interface{}, error)

// Writing Transactions
//
Expand Down Expand Up @@ -195,7 +195,7 @@
func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) {
hexTx := hash.Hex()
e.logger.Debug("eth_getTransactionReceipt", "hash", hexTx)
return e.backend.GetTransactionReceipt(hash)
return e.backend.GetTransactionReceipt(hash, nil)

Check warning on line 198 in rpc/namespaces/ethereum/eth/api.go

View check run for this annotation

Codecov / codecov/patch

rpc/namespaces/ethereum/eth/api.go#L198

Added line #L198 was not covered by tests
}

// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash.
Expand All @@ -222,6 +222,12 @@
return e.backend.GetTransactionByBlockNumberAndIndex(blockNum, idx)
}

// GetBlockReceipts returns a list of transaction receipts given a block number.
func (e *PublicAPI) GetBlockReceipts(blockNum rpctypes.BlockNumber) ([]map[string]interface{}, error) {
e.logger.Debug("eth_getBlockReceipts", "number", blockNum)
return e.backend.GetBlockReceipts(blockNum)

Check warning on line 228 in rpc/namespaces/ethereum/eth/api.go

View check run for this annotation

Codecov / codecov/patch

rpc/namespaces/ethereum/eth/api.go#L226-L228

Added lines #L226 - L228 were not covered by tests
}

///////////////////////////////////////////////////////////////////////////////
/// Write Txs ///
///////////////////////////////////////////////////////////////////////////////
Expand Down
Loading
Loading