From b38c5166bcba9247d40417007792db96187f899e Mon Sep 17 00:00:00 2001 From: evgeniy-scherbina Date: Mon, 6 May 2024 17:17:28 -0400 Subject: [PATCH] Initialize precompiles in UpdateParams --- x/evm/keeper/msg_server.go | 56 +++++++++++++++++++++--- x/evm/keeper/msg_server_test.go | 76 +++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 5 deletions(-) diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 5a51dd49f3..638c7cea09 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -19,21 +19,27 @@ import ( "context" "encoding/json" "fmt" + "math/big" "strconv" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - - tmbytes "github.com/cometbft/cometbft/libs/bytes" - tmtypes "github.com/cometbft/cometbft/types" - errorsmod "cosmossdk.io/errors" "github.com/armon/go-metrics" + tmbytes "github.com/cometbft/cometbft/libs/bytes" + tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/types" ) +const PrecompileNonce uint64 = 1 + +var PrecompileCode = []byte{0x1} + var _ types.MsgServer = &Keeper{} // EthereumTx implements the gRPC MsgServer interface. It receives a transaction which is then @@ -153,6 +159,46 @@ func (k *Keeper) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) } ctx := sdk.UnwrapSDKContext(goCtx) + + oldParams := k.GetParams(ctx) + oldEnabledPrecompiles := oldParams.GetEnabledPrecompiles() + + newEnabledPrecompiles := req.Params.EnabledPrecompiles + newEnabledPrecompilesMap := make(map[common.Address]struct{}, len(newEnabledPrecompiles)) + + for _, hexAddr := range newEnabledPrecompiles { + addr := common.HexToAddress(hexAddr) + newEnabledPrecompilesMap[addr] = struct{}{} + + // Set the nonce of the precompile's address (as is done when a contract is created) to ensure + // that it is marked as non-empty and will not be cleaned up when the statedb is finalized. + codeHash := crypto.Keccak256Hash(PrecompileCode) + err := k.SetAccount(ctx, addr, statedb.Account{ + Nonce: PrecompileNonce, + Balance: big.NewInt(0), + CodeHash: codeHash[:], + }) + if err != nil { + return nil, err + } + + // Set the code of the precompile's address to a non-zero length byte slice to ensure that the precompile + // can be called from within Solidity contracts. Solidity adds a check before invoking a contract to ensure + // that it does not attempt to invoke a non-existent contract. + k.SetCode(ctx, codeHash[:], PrecompileCode) + } + + for _, hexAddr := range oldEnabledPrecompiles { + addr := common.HexToAddress(hexAddr) + if _, ok := newEnabledPrecompilesMap[addr]; ok { + continue + } + + if err := k.DeleteAccount(ctx, addr); err != nil { + return nil, err + } + } + if err := k.SetParams(ctx, req.Params); err != nil { return nil, err } diff --git a/x/evm/keeper/msg_server_test.go b/x/evm/keeper/msg_server_test.go index 4a8c86cefa..e9062d4605 100644 --- a/x/evm/keeper/msg_server_test.go +++ b/x/evm/keeper/msg_server_test.go @@ -3,8 +3,10 @@ package keeper_test import ( "math/big" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" @@ -12,6 +14,10 @@ import ( "github.com/evmos/ethermint/x/evm/types" ) +const PrecompileNonce uint64 = 1 + +var PrecompileCode = []byte{0x1} + func (suite *KeeperTestSuite) TestEthereumTx() { var ( err error @@ -115,3 +121,73 @@ func (suite *KeeperTestSuite) TestUpdateParams() { }) } } + +func (suite *KeeperTestSuite) TestInitPrecompiles() { + addr1 := "0x1000000000000000000000000000000000000000" + addr2 := "0x2000000000000000000000000000000000000000" + addr3 := "0x3000000000000000000000000000000000000000" + + testCases := []struct { + name string + enabledPrecompiles []string + // precompiles which must be uninitialized after corresponding test case + uninitialized []string + }{ + { + name: "enable addr1 and addr2", + enabledPrecompiles: []string{addr1, addr2}, + uninitialized: []string{addr3}, + }, + { + name: "enable addr3, and disable the rest", + enabledPrecompiles: []string{addr3}, + uninitialized: []string{addr1, addr2}, + }, + { + name: "no changes", + enabledPrecompiles: []string{addr3}, + uninitialized: []string{addr1, addr2}, + }, + { + name: "enable all precompiles", + enabledPrecompiles: []string{addr1, addr2, addr3}, + uninitialized: []string{}, + }, + { + name: "disable all precompiles", + enabledPrecompiles: []string{}, + uninitialized: []string{addr1, addr2, addr3}, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + params := suite.app.EvmKeeper.GetParams(suite.ctx) + params.EnabledPrecompiles = tc.enabledPrecompiles + + _, err := suite.app.EvmKeeper.UpdateParams(sdk.WrapSDKContext(suite.ctx), &types.MsgUpdateParams{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Params: params, + }) + suite.Require().NoError(err) + + vmdb := suite.StateDB() + + // check that precompiles are initialized + for _, hexAddr := range tc.enabledPrecompiles { + addr := common.HexToAddress(hexAddr) + + suite.Require().Equal(PrecompileNonce, vmdb.GetNonce(addr)) + suite.Require().Equal(PrecompileCode, vmdb.GetCode(addr)) + } + + // check that precompiles are uninitialized + for _, hexAddr := range tc.uninitialized { + addr := common.HexToAddress(hexAddr) + + suite.Require().Equal(uint64(0), vmdb.GetNonce(addr)) + suite.Require().Equal([]byte(nil), vmdb.GetCode(addr)) + } + }) + } +}