Skip to content
Open
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 @@ -73,6 +73,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (evm) [#921](https://github.com/crypto-org-chain/ethermint/pull/921) fix: enforce floor-data-gas
* (test) [#926](https://github.com/crypto-org-chain/ethermint/pull/926) fix(test): remove flaky `base_fee` assertion in `update_feemarket_param`.
* (server) [#946](https://github.com/crypto-org-chain/ethermint/pull/946) feat(server): make JSON-RPC batch limits configurable.
* (rpc) [#965](https://github.com/crypto-org-chain/ethermint/pull/965) fix(rpc): align `eth_getBlockReceipts` response with execution-apis spec.
Comment thread
XinyuCRO marked this conversation as resolved.
* (geth) [#957](https://github.com/crypto-org-chain/ethermint/pull/957) feat(geth): update go-ethereum version to `v1.16.9`, enable Osaka hardfork

## [v0.23.0] - 2026-01-13
Expand Down
2 changes: 1 addition & 1 deletion rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +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)
GetBlockReceipts(blockNrOrHash rpctypes.BlockNumberOrHash) ([]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
20 changes: 16 additions & 4 deletions rpc/backend/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ func (b *Backend) GetBlockByNumber(blockNum rpctypes.BlockNumber, fullTx bool) (
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)
// GetBlockReceipts returns a list of Ethereum transaction receipts given a block number or hash.
func (b *Backend) GetBlockReceipts(blockNrOrHash rpctypes.BlockNumberOrHash) ([]map[string]interface{}, error) {
resBlock, err := b.tendermintBlockByNumberOrHash(blockNrOrHash)
if err != nil {
return nil, nil
}
Expand All @@ -103,7 +103,7 @@ func (b *Backend) GetBlockReceipts(blockNum rpctypes.BlockNumber) ([]map[string]
}
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())
b.logger.Debug("failed to fetch block result from Tendermint", "block", blockNrOrHash, "error", err.Error())
return nil, err
}

Expand All @@ -127,6 +127,18 @@ func (b *Backend) GetBlockReceipts(blockNum rpctypes.BlockNumber) ([]map[string]
return res, nil
}

func (b *Backend) tendermintBlockByNumberOrHash(blockNrOrHash rpctypes.BlockNumberOrHash) (*tmrpctypes.ResultBlock, error) {
if blockNrOrHash.BlockHash != nil {
return b.TendermintBlockByHash(*blockNrOrHash.BlockHash)
}
if blockNrOrHash.BlockNumber != nil {
return b.TendermintBlockByNumber(*blockNrOrHash.BlockNumber)
}
b.logger.Debug("empty block number/hash, defaulting eth_getBlockReceipts to latest")
blockNum := rpctypes.EthLatestBlockNumber
return b.TendermintBlockByNumber(blockNum)
Comment thread
XinyuCRO marked this conversation as resolved.
}

// 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 Down
3 changes: 2 additions & 1 deletion rpc/backend/blocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1733,7 +1733,8 @@ func (suite *BackendTestSuite) TestEthBlockReceipts() {
err := suite.backend.indexer.IndexBlock(tc.block, tc.blockResult)
suite.Require().NoError(err)

receipts, err := suite.backend.GetBlockReceipts(ethrpc.BlockNumber(1))
blockNum := ethrpc.BlockNumber(1)
receipts, err := suite.backend.GetBlockReceipts(ethrpc.BlockNumberOrHash{BlockNumber: &blockNum})

for receipt := range receipts {
if tc.expPass {
Expand Down
6 changes: 3 additions & 3 deletions rpc/backend/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,18 +255,18 @@ func RegisterBlockByHash(
block := types.MakeBlock(1, []types.Tx{tx}, nil, nil)
resBlock := &tmrpctypes.ResultBlock{Block: block}

client.On("BlockByHash", rpc.ContextWithHeight(1), []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}).
client.On("BlockByHash", rpc.ContextWithHeight(1), hash.Bytes()).
Return(resBlock, nil)
return resBlock, nil
}

func RegisterBlockByHashError(client *mocks.Client, hash common.Hash, tx []byte) {
client.On("BlockByHash", rpc.ContextWithHeight(1), []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}).
client.On("BlockByHash", rpc.ContextWithHeight(1), hash.Bytes()).
Return(nil, errortypes.ErrInvalidRequest)
}

func RegisterBlockByHashNotFound(client *mocks.Client, hash common.Hash, tx []byte) {
client.On("BlockByHash", rpc.ContextWithHeight(1), []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}).
client.On("BlockByHash", rpc.ContextWithHeight(1), hash.Bytes()).
Return(nil, nil)
}

Expand Down
21 changes: 17 additions & 4 deletions rpc/backend/tx_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package backend
import (
"encoding/json"
"fmt"
"math/big"

errorsmod "cosmossdk.io/errors"
abci "github.com/cometbft/cometbft/abci/types"
Expand Down Expand Up @@ -472,15 +473,27 @@ func (b *Backend) buildReceiptDirect(
receipt["contractAddress"] = crypto.CreateAddress(from, txData.Nonce())
}

if txData.Type() == ethtypes.DynamicFeeTxType {
baseFee, err := b.BaseFee(blockResults)
if txData.Type() == ethtypes.BlobTxType {
// Ethermint does not execute EIP-4844 blob gas accounting yet.
receipt["blobGasUsed"] = hexutil.Uint64(0)
Comment thread
XinyuCRO marked this conversation as resolved.
receipt["blobGasPrice"] = (*hexutil.Big)(big.NewInt(0))
}

var baseFee *big.Int
if txData.Type() == ethtypes.DynamicFeeTxType || txData.Type() == ethtypes.BlobTxType || txData.Type() == ethtypes.SetCodeTxType {
var err error
baseFee, err = b.BaseFee(blockResults)
if err != nil {
// 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(*ethMsg.GetEffectiveGasPrice(baseFee))
baseFee = nil
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When BaseFee() returns nil for a fee-market tx (whether due to pruning or fee market being disabled), the receipt should not contain effectiveGasPrice

}
}
effectiveGasPrice := ethMsg.GetEffectiveGasPrice(baseFee)
if effectiveGasPrice == nil {
return nil, errorsmod.Wrap(errortypes.ErrLogic, "effective gas price is nil")
}
receipt["effectiveGasPrice"] = hexutil.Big(*effectiveGasPrice)

return receipt, nil
}
Expand Down
102 changes: 99 additions & 3 deletions rpc/backend/tx_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,8 @@ func (suite *BackendTestSuite) TestGetTransactionReceipt_BlockScopedWhenIndexerO
suite.Require().NotNil(receipt)
suite.Require().Equal(hexutil.Uint64(1), receipt["blockNumber"])

receipts, err := suite.backend.GetBlockReceipts(rpctypes.BlockNumber(1))
blockNum := rpctypes.BlockNumber(1)
receipts, err := suite.backend.GetBlockReceipts(rpctypes.BlockNumberOrHash{BlockNumber: &blockNum})
suite.Require().NoError(err)
suite.Require().Len(receipts, 1)
suite.Require().Equal(hexutil.Uint64(1), receipts[0]["blockNumber"])
Expand Down Expand Up @@ -881,7 +882,8 @@ func (suite *BackendTestSuite) TestGetBlockReceipts_BlockGasExceededWithoutIndex
Return(blockRes, nil)
suite.backend.indexer = nil

receipts, err := suite.backend.GetBlockReceipts(rpctypes.BlockNumber(1))
blockNum := rpctypes.BlockNumber(1)
receipts, err := suite.backend.GetBlockReceipts(rpctypes.BlockNumberOrHash{BlockNumber: &blockNum})
suite.Require().NoError(err)
suite.Require().Len(receipts, 2)

Expand Down Expand Up @@ -923,12 +925,106 @@ func (suite *BackendTestSuite) TestGetBlockReceipts_IgnoresIndexerHashMismatch()
Return(blockRes, nil)
suite.backend.indexer = failingLookupIndexer{}

receipts, err := suite.backend.GetBlockReceipts(rpctypes.BlockNumber(1))
blockNum := rpctypes.BlockNumber(1)
receipts, err := suite.backend.GetBlockReceipts(rpctypes.BlockNumberOrHash{BlockNumber: &blockNum})
suite.Require().NoError(err)
suite.Require().Len(receipts, 1)
suite.Require().Equal(msgEthereumTx.Hash(), receipts[0]["transactionHash"])
}

func (suite *BackendTestSuite) TestGetBlockReceipts_ByHash() {
msgEthereumTx, txBz := suite.buildEthereumTxWithNonceAndGas(0, 45000)
hash := common.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")

client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
var header metadata.MD
RegisterParams(queryClient, &header, 1)
RegisterParamsWithoutHeader(queryClient, 1)
RegisterBlockByHash(client, hash, txBz)

blockRes := &tmrpctypes.ResultBlockResults{
Height: 1,
TxsResults: []*abci.ExecTxResult{
{
Code: 11,
Log: rpctypes.ExceedBlockGasLimitError,
},
},
}
client.On("BlockResults", rpctypes.ContextWithHeight(1), mock.AnythingOfType("*int64")).
Return(blockRes, nil)

receipts, err := suite.backend.GetBlockReceipts(rpctypes.BlockNumberOrHash{BlockHash: &hash})
suite.Require().NoError(err)
suite.Require().Len(receipts, 1)
suite.Require().Equal(msgEthereumTx.Hash(), receipts[0]["transactionHash"])
}

func (suite *BackendTestSuite) TestGetBlockReceipts_EmptyBlockNumberOrHashDefaultsToLatest() {
msgEthereumTx, txBz := suite.buildEthereumTxWithNonceAndGas(0, 45000)

client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
var header metadata.MD
RegisterParams(queryClient, &header, 1)
RegisterParamsWithoutHeader(queryClient, 1)
_, err := RegisterBlockMultipleTxs(client, 1, []types.Tx{txBz})
suite.Require().NoError(err)

blockRes := &tmrpctypes.ResultBlockResults{
Height: 1,
TxsResults: []*abci.ExecTxResult{
{
Code: 11,
Log: rpctypes.ExceedBlockGasLimitError,
},
},
}
client.On("BlockResults", rpctypes.ContextWithHeight(1), mock.AnythingOfType("*int64")).
Return(blockRes, nil)

receipts, err := suite.backend.GetBlockReceipts(rpctypes.BlockNumberOrHash{})
suite.Require().NoError(err)
suite.Require().Len(receipts, 1)
suite.Require().Equal(msgEthereumTx.Hash(), receipts[0]["transactionHash"])
}

func (suite *BackendTestSuite) TestBuildReceiptDirect_SetCodeTxEffectiveGasPrice() {
msgSetCodeTx := suite.buildSetCodeTx()

queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
var header metadata.MD
RegisterParams(queryClient, &header, 1)
RegisterParamsWithoutHeader(queryClient, 1)
RegisterBaseFee(queryClient, sdkmath.NewInt(1))

block := &tmrpctypes.ResultBlock{
Block: types.MakeBlock(1, nil, nil, nil),
}
blockResults := &tmrpctypes.ResultBlockResults{
Height: 1,
TxsResults: []*abci.ExecTxResult{
{
Code: 0,
GasUsed: 21000,
},
},
}
txResult := &ethermint.TxResult{
Height: 1,
TxIndex: 0,
MsgIndex: 0,
EthTxIndex: 0,
GasUsed: 21000,
CumulativeGasUsed: 21000,
}

receipt, err := suite.backend.buildReceiptDirect(block, blockResults, txResult, msgSetCodeTx)
suite.Require().NoError(err)
suite.Require().Equal(hexutil.Big(*big.NewInt(10001)), receipt["effectiveGasPrice"])
}

func (suite *BackendTestSuite) TestGetGasUsed() {
origin := suite.backend.cfg.JSONRPC.FixRevertGasRefundHeight
testCases := []struct {
Expand Down
10 changes: 5 additions & 5 deletions rpc/namespaces/ethereum/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type EthereumAPI interface {
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)
GetBlockReceipts(blockNum rpctypes.BlockNumber) ([]map[string]interface{}, error)
GetBlockReceipts(blockNrOrHash rpctypes.BlockNumberOrHash) ([]map[string]interface{}, error)

// Writing Transactions
//
Expand Down Expand Up @@ -225,10 +225,10 @@ func (e *PublicAPI) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockN
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)
// GetBlockReceipts returns a list of transaction receipts given a block number or hash.
func (e *PublicAPI) GetBlockReceipts(blockNrOrHash rpctypes.BlockNumberOrHash) ([]map[string]interface{}, error) {
e.logger.Debug("eth_getBlockReceipts", "block", blockNrOrHash)
return e.backend.GetBlockReceipts(blockNrOrHash)
}

///////////////////////////////////////////////////////////////////////////////
Expand Down
4 changes: 2 additions & 2 deletions rpc/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func ContextWithHeight(height int64) context.Context {
}

// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
// - "latest", "finalized", "earliest" or "pending" as string arguments
// - "latest", "finalized", "safe", "earliest" or "pending" as string arguments
// - the block number
// Returned errors:
// - an invalid block number error when the given argument isn't a known strings
Expand Down Expand Up @@ -174,7 +174,7 @@ func (bnh *BlockNumberOrHash) decodeFromString(input string) error {
case BlockParamEarliest:
bn := EthEarliestBlockNumber
bnh.BlockNumber = &bn
case BlockParamLatest, BlockParamFinalized:
case BlockParamLatest, BlockParamFinalized, BlockParamSafe:
bn := EthLatestBlockNumber
bnh.BlockNumber = &bn
case BlockParamPending:
Expand Down
9 changes: 9 additions & 0 deletions rpc/types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ func TestUnmarshalBlockNumberOrHash(t *testing.T) {
},
true,
},
{
"String input with block number safe",
[]byte("\"safe\""),
func() {
require.Equal(t, *bnh.BlockNumber, EthLatestBlockNumber)
require.Nil(t, bnh.BlockHash)
},
true,
},
{
"String input with block number overflow",
[]byte("\"0xffffffffffffffffffffffffffffffffffffff\""),
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests/expected_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
"blockNumber": "0xa50131",
"contractAddress": None,
"cumulativeGasUsed": "0xb72e24",
# "effectiveGasPrice": "0x147d357000",
"effectiveGasPrice": "0x147d357000",
"from": "0x56ac35db407fe1fb4977edd41f712aa5d8e7eb08",
"gasUsed": "0x5208",
"logs": [],
Expand Down
Loading