diff --git a/CHANGELOG.md b/CHANGELOG.md index 400cc0827f..3ee7a58b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (rpc) [#516](https://github.com/crypto-org-chain/ethermint/pull/516) Avoid method eth_chainId crashed due to nil pointer on IsEIP155 check. * (cli) [#524](https://github.com/crypto-org-chain/ethermint/pull/524) Allow tx evm raw run for generate only when offline with evm-denom flag. * (rpc) [#527](https://github.com/crypto-org-chain/ethermint/pull/527) Fix balance consistency between trace-block and state machine. +* (rpc) [#534](https://github.com/crypto-org-chain/ethermint/pull/534) Fix opBlockhash when no block header in abci request. ### Improvements diff --git a/app/app.go b/app/app.go index f3033bbba6..f08c9fcade 100644 --- a/app/app.go +++ b/app/app.go @@ -1048,6 +1048,7 @@ func (app *EthermintApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config. // RegisterTxService implements the Application.RegisterTxService method. func (app *EthermintApp) RegisterTxService(clientCtx client.Context) { + app.EvmKeeper.WithCometClient(clientCtx.Client) authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry) } diff --git a/tests/integration_tests/hardhat/contracts/TestBlockTxProperties.sol b/tests/integration_tests/hardhat/contracts/TestBlockTxProperties.sol new file mode 100644 index 0000000000..07d5b0d20d --- /dev/null +++ b/tests/integration_tests/hardhat/contracts/TestBlockTxProperties.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract TestBlockTxProperties { + function getBlockHash(uint256 blockNumber) public view returns (bytes32) { + return blockhash(blockNumber); + } +} diff --git a/tests/integration_tests/test_block.py b/tests/integration_tests/test_block.py new file mode 100644 index 0000000000..b8c1facc32 --- /dev/null +++ b/tests/integration_tests/test_block.py @@ -0,0 +1,11 @@ +from .utils import CONTRACTS, deploy_contract, w3_wait_for_new_blocks + + +def test_call(ethermint): + w3 = ethermint.w3 + contract, res = deploy_contract(w3, CONTRACTS["TestBlockTxProperties"]) + height = w3.eth.get_block_number() + w3_wait_for_new_blocks(w3, 1) + res = contract.caller.getBlockHash(height).hex() + blk = w3.eth.get_block(height) + assert f"0x{res}" == blk.hash.hex(), res diff --git a/tests/integration_tests/utils.py b/tests/integration_tests/utils.py index 4eb1fdcfa3..b450c99eeb 100644 --- a/tests/integration_tests/utils.py +++ b/tests/integration_tests/utils.py @@ -43,6 +43,7 @@ "Calculator": "Calculator.sol", "Caller": "Caller.sol", "Random": "Random.sol", + "TestBlockTxProperties": "TestBlockTxProperties.sol", } diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 39997b685b..e92fa7d6a0 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -21,6 +21,8 @@ import ( errorsmod "cosmossdk.io/errors" "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -73,6 +75,8 @@ type Keeper struct { // Legacy subspace ss paramstypes.Subspace customContractFns []CustomContractFn + + signClient tmrpcclient.SignClient } // NewKeeper generates new evm module keeper @@ -144,6 +148,14 @@ func (k Keeper) ChainID() *big.Int { return k.eip155ChainID } +func (k *Keeper) WithCometClient(c client.CometRPC) { + sc, ok := c.(tmrpcclient.SignClient) + if !ok { + panic("invalid rpc client") + } + k.signClient = sc +} + // ---------------------------------------------------------------------------- // Block Bloom // Required by Web3 API. diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index d8eaccc49c..e559a6252d 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -128,19 +128,11 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { case ctx.BlockHeight() > h: // Case 2: if the chain is not the current height we need to retrieve the hash from the store for the // current chain epoch. This only applies if the current height is greater than the requested height. - histInfo, err := k.stakingKeeper.GetHistoricalInfo(ctx, h) + res, err := k.signClient.Header(ctx, &h) if err != nil { - k.Logger(ctx).Debug("historical info not found", "height", h, "err", err.Error()) return common.Hash{} } - - header, err := cmttypes.HeaderFromProto(&histInfo.Header) - if err != nil { - k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err) - return common.Hash{} - } - - return common.BytesToHash(header.Hash()) + return common.BytesToHash(res.Header.Hash()) default: // Case 3: heights greater than the current one returns an empty hash. return common.Hash{} diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index bdc95a1f87..f8729d878c 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -10,6 +10,7 @@ import ( storetypes "cosmossdk.io/store/types" "github.com/cometbft/cometbft/crypto/tmhash" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" tmtypes "github.com/cometbft/cometbft/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -22,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/evmos/ethermint/app" + "github.com/evmos/ethermint/rpc/backend/mocks" "github.com/evmos/ethermint/tests" "github.com/evmos/ethermint/testutil" utiltx "github.com/evmos/ethermint/testutil/tx" @@ -92,13 +94,13 @@ func (suite *StateTransitionTestSuite) TestGetHashFn() { testCases := []struct { msg string height uint64 - malleate func() + malleate func(height uint64) expHash common.Hash }{ { "case 1.1: context hash cached", uint64(suite.Ctx.BlockHeight()), - func() { + func(_ uint64) { suite.Ctx = suite.Ctx.WithHeaderHash(tmhash.Sum([]byte("header"))).WithConsensusParams(*testutil.DefaultConsensusParams) }, common.BytesToHash(tmhash.Sum([]byte("header"))), @@ -106,54 +108,48 @@ func (suite *StateTransitionTestSuite) TestGetHashFn() { { "case 1.2: failed to cast Tendermint header", uint64(suite.Ctx.BlockHeight()), - func() { - header := tmproto.Header{} + func(_ uint64) { header.Height = suite.Ctx.BlockHeight() - suite.Ctx = suite.Ctx.WithBlockHeader(header).WithConsensusParams(*testutil.DefaultConsensusParams) + suite.Ctx = suite.Ctx.WithBlockHeader(tmproto.Header{}).WithConsensusParams(*testutil.DefaultConsensusParams) }, common.Hash{}, }, { "case 1.3: hash calculated from Tendermint header", uint64(suite.Ctx.BlockHeight()), - func() { + func(_ uint64) { suite.Ctx = suite.Ctx.WithBlockHeader(header).WithConsensusParams(*testutil.DefaultConsensusParams) }, common.BytesToHash(hash), }, { - "case 2.1: height lower than current one, hist info not found", - 1, - func() { - suite.Ctx = suite.Ctx.WithBlockHeight(10).WithConsensusParams(*testutil.DefaultConsensusParams) - }, - common.Hash{}, - }, - { - "case 2.2: height lower than current one, invalid hist info header", + "case 2.1: height lower than current one, header not found", 1, - func() { - suite.App.StakingKeeper.SetHistoricalInfo(suite.Ctx, 1, &stakingtypes.HistoricalInfo{}) + func(height uint64) { + c := mocks.NewClient(suite.T()) + suite.App.EvmKeeper.WithCometClient(c) suite.Ctx = suite.Ctx.WithBlockHeight(10).WithConsensusParams(*testutil.DefaultConsensusParams) + height0 := int64(height) + c.On("Header", suite.Ctx, &height0).Return(&tmrpctypes.ResultHeader{Header: &h}, nil) }, common.Hash{}, }, { - "case 2.3: height lower than current one, calculated from hist info header", + "case 2.2: height lower than current one, header found", 1, - func() { - histInfo := &stakingtypes.HistoricalInfo{ - Header: header, - } - suite.App.StakingKeeper.SetHistoricalInfo(suite.Ctx, 1, histInfo) + func(height uint64) { + c := mocks.NewClient(suite.T()) + suite.App.EvmKeeper.WithCometClient(c) suite.Ctx = suite.Ctx.WithBlockHeight(10).WithConsensusParams(*testutil.DefaultConsensusParams) + height0 := int64(height) + c.On("Header", suite.Ctx, &height0).Return(&tmrpctypes.ResultHeader{Header: &h}, nil) }, common.BytesToHash(hash), }, { "case 3: height greater than current one", 200, - func() {}, + func(_ uint64) {}, common.Hash{}, }, } @@ -162,7 +158,7 @@ func (suite *StateTransitionTestSuite) TestGetHashFn() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - tc.malleate() + tc.malleate(tc.height) hash := suite.App.EvmKeeper.GetHashFn(suite.Ctx)(tc.height) suite.Require().Equal(tc.expHash, hash)