From b6ef4f5c11c44353482f86d6e3dfa5c56b6117e1 Mon Sep 17 00:00:00 2001 From: evgeniy-scherbina Date: Thu, 18 Apr 2024 13:40:24 -0400 Subject: [PATCH 1/2] Remove VM wrapper for custom precompiles --- app/ante/eth.go | 2 +- app/ante/interfaces.go | 3 +- app/app.go | 11 ++-- x/evm/keeper/keeper.go | 32 +++++------ x/evm/keeper/state_transition.go | 18 +++--- x/evm/types/interfaces.go | 17 +++++- x/evm/vm/geth/geth.go | 95 -------------------------------- x/evm/vm/geth/precompiles.go | 42 -------------- x/evm/vm/interface.go | 85 ---------------------------- 9 files changed, 42 insertions(+), 263 deletions(-) delete mode 100644 x/evm/vm/geth/geth.go delete mode 100644 x/evm/vm/geth/precompiles.go delete mode 100644 x/evm/vm/interface.go diff --git a/app/ante/eth.go b/app/ante/eth.go index 2402167be7..cf949eb672 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -307,7 +307,7 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate // check that caller has enough balance to cover asset transfer for **topmost** call // NOTE: here the gas consumed is from the context with the infinite gas meter - if coreMsg.Value().Sign() > 0 && !evm.Context().CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) { + if coreMsg.Value().Sign() > 0 && !evm.Context.CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) { return ctx, errorsmod.Wrapf( errortypes.ErrInsufficientFunds, "failed to transfer %s from address %s using the EVM block context transfer function", diff --git a/app/ante/interfaces.go b/app/ante/interfaces.go index e48e0a50a4..99ea1d9b4b 100644 --- a/app/ante/interfaces.go +++ b/app/ante/interfaces.go @@ -28,7 +28,6 @@ import ( "github.com/evmos/ethermint/x/evm/statedb" evmtypes "github.com/evmos/ethermint/x/evm/types" - evm "github.com/evmos/ethermint/x/evm/vm" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" ) @@ -44,7 +43,7 @@ type EVMKeeper interface { statedb.Keeper DynamicFeeEVMKeeper - NewEVM(ctx sdk.Context, msg core.Message, cfg *statedb.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB) evm.EVM + NewEVM(ctx sdk.Context, msg core.Message, cfg *statedb.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB) *vm.EVM DeductTxCostsFromUserBalance(ctx sdk.Context, fees sdk.Coins, from common.Address) error GetBalance(ctx sdk.Context, addr common.Address) *big.Int ResetTransientGasUsed(ctx sdk.Context) diff --git a/app/app.go b/app/app.go index 91f78d6219..1df647ea7a 100644 --- a/app/app.go +++ b/app/app.go @@ -23,14 +23,14 @@ import ( "os" "path/filepath" - "github.com/gorilla/mux" - "github.com/rakyll/statik/fs" - "github.com/spf13/cast" - dbm "github.com/cometbft/cometbft-db" abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/gorilla/mux" + "github.com/rakyll/statik/fs" + "github.com/spf13/cast" "cosmossdk.io/simapp" simappparams "cosmossdk.io/simapp/params" @@ -127,7 +127,6 @@ import ( evmkeeper "github.com/evmos/ethermint/x/evm/keeper" evmtypes "github.com/evmos/ethermint/x/evm/types" legacyevmtypes "github.com/evmos/ethermint/x/evm/types/legacy" - "github.com/evmos/ethermint/x/evm/vm/geth" "github.com/evmos/ethermint/x/feemarket" feemarketkeeper "github.com/evmos/ethermint/x/feemarket/keeper" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" @@ -427,7 +426,7 @@ func NewEthermintApp( appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, stakingKeeper, app.FeeMarketKeeper, - nil, geth.NewEVM, tracer, evmSs, + vm.NewEVM, tracer, evmSs, ) // Create IBC Keeper diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index e9528b75a3..90ec6038b4 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -35,7 +35,6 @@ import ( "github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/types" legacytypes "github.com/evmos/ethermint/x/evm/types/legacy" - evm "github.com/evmos/ethermint/x/evm/vm" ) // Keeper grants access to the EVM module state and implements the go-ethereum StateDB interface. @@ -72,11 +71,8 @@ type Keeper struct { // EVM Hooks for tx post-processing hooks types.EvmHooks - // custom stateless precompiled smart contracts - customPrecompiles evm.PrecompiledContracts - // evm constructor function - evmConstructor evm.Constructor + evmConstructor types.Constructor // Legacy subspace ss paramstypes.Subspace } @@ -90,8 +86,7 @@ func NewKeeper( bankKeeper types.BankKeeper, sk types.StakingKeeper, fmk types.FeeMarketKeeper, - customPrecompiles evm.PrecompiledContracts, - evmConstructor evm.Constructor, + evmConstructor types.Constructor, tracer string, ss paramstypes.Subspace, ) *Keeper { @@ -111,18 +106,17 @@ func NewKeeper( // NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations return &Keeper{ - cdc: cdc, - authority: authority, - accountKeeper: ak, - bankKeeper: bankKeeper, - stakingKeeper: sk, - feeMarketKeeper: fmk, - storeKey: storeKey, - transientKey: transientKey, - customPrecompiles: customPrecompiles, - evmConstructor: evmConstructor, - tracer: tracer, - ss: ss, + cdc: cdc, + authority: authority, + accountKeeper: ak, + bankKeeper: bankKeeper, + stakingKeeper: sk, + feeMarketKeeper: fmk, + storeKey: storeKey, + transientKey: transientKey, + evmConstructor: evmConstructor, + tracer: tracer, + ss: ss, } } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 4fd7f4c1c2..663f5a6d74 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -23,17 +23,15 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - ethermint "github.com/evmos/ethermint/types" - "github.com/evmos/ethermint/x/evm/statedb" - "github.com/evmos/ethermint/x/evm/types" - evm "github.com/evmos/ethermint/x/evm/vm" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + ethermint "github.com/evmos/ethermint/types" + "github.com/evmos/ethermint/x/evm/statedb" + "github.com/evmos/ethermint/x/evm/types" ) // NewEVM generates a go-ethereum VM from the provided Message fields and the chain parameters @@ -51,7 +49,7 @@ func (k *Keeper) NewEVM( cfg *statedb.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB, -) evm.EVM { +) *vm.EVM { blockCtx := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -70,7 +68,7 @@ func (k *Keeper) NewEVM( tracer = k.Tracer(ctx, msg, cfg.ChainConfig) } vmConfig := k.VMConfig(ctx, msg, cfg, tracer) - return k.evmConstructor(blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig, k.customPrecompiles) + return k.evmConstructor(blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig) } // GetHashFn implements vm.GetHashFunc for Ethermint. It handles 3 cases: @@ -335,7 +333,7 @@ func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context, leftoverGas := msg.Gas() // Allow the tracer captures the tx level events, mainly the gas consumption. - vmCfg := evm.Config() + vmCfg := evm.Config if vmCfg.Debug { vmCfg.Tracer.CaptureTxStart(leftoverGas) defer func() { @@ -345,7 +343,7 @@ func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context, sender := vm.AccountRef(msg.From()) contractCreation := msg.To() == nil - isLondon := cfg.ChainConfig.IsLondon(evm.Context().BlockNumber) + isLondon := cfg.ChainConfig.IsLondon(evm.Context.BlockNumber) intrinsicGas, err := k.GetEthIntrinsicGas(ctx, msg, cfg.ChainConfig, contractCreation) if err != nil { @@ -363,7 +361,7 @@ func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context, // access list preparation is moved from ante handler to here, because it's needed when `ApplyMessage` is called // under contexts where ante handlers are not run, for example `eth_call` and `eth_estimateGas`. if rules := cfg.ChainConfig.Rules(big.NewInt(ctx.BlockHeight()), cfg.ChainConfig.MergeNetsplitBlock != nil); rules.IsBerlin { - stateDB.PrepareAccessList(msg.From(), msg.To(), evm.ActivePrecompiles(rules), msg.AccessList()) + stateDB.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) } if contractCreation { diff --git a/x/evm/types/interfaces.go b/x/evm/types/interfaces.go index 62c6a08134..caddeb78c9 100644 --- a/x/evm/types/interfaces.go +++ b/x/evm/types/interfaces.go @@ -18,14 +18,15 @@ package types import ( "math/big" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" ) @@ -83,3 +84,13 @@ type ( WithKeyTable(table paramtypes.KeyTable) paramtypes.Subspace } ) + +// Constructor defines the function used to instantiate the EVM on +// each state transition. +type Constructor func( + blockCtx vm.BlockContext, + txCtx vm.TxContext, + stateDB vm.StateDB, + chainConfig *params.ChainConfig, + config vm.Config, +) *vm.EVM diff --git a/x/evm/vm/geth/geth.go b/x/evm/vm/geth/geth.go deleted file mode 100644 index ebbf8743db..0000000000 --- a/x/evm/vm/geth/geth.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2021 Evmos Foundation -// This file is part of Evmos' Ethermint library. -// -// The Ethermint library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Ethermint library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE -package geth - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/precompile/contract" - - evm "github.com/evmos/ethermint/x/evm/vm" -) - -var ( - _ evm.EVM = (*EVM)(nil) - _ evm.Constructor = NewEVM -) - -// EVM is the wrapper for the go-ethereum EVM. -type EVM struct { - *vm.EVM -} - -// NewEVM defines the constructor function for the go-ethereum (geth) EVM. It uses -// the default precompiled contracts and the EVM concrete implementation from -// geth. -func NewEVM( - blockCtx vm.BlockContext, - txCtx vm.TxContext, - stateDB vm.StateDB, - chainConfig *params.ChainConfig, - config vm.Config, - _ evm.PrecompiledContracts, // unused -) evm.EVM { - return &EVM{ - EVM: vm.NewEVM(blockCtx, txCtx, stateDB, chainConfig, config), - } -} - -// Context returns the EVM's Block Context -func (e EVM) Context() vm.BlockContext { - return e.EVM.Context -} - -// TxContext returns the EVM's Tx Context -func (e EVM) TxContext() vm.TxContext { - return e.EVM.TxContext -} - -// Config returns the configuration options for the EVM. -func (e EVM) Config() vm.Config { - return e.EVM.Config -} - -// Precompile returns the precompiled contract associated with the given address -// and the current chain configuration. If the contract cannot be found it returns -// nil. -func (e EVM) Precompile(addr common.Address) (p contract.StatefulPrecompiledContract, found bool) { - precompiles := GetPrecompiles(e.ChainConfig(), e.EVM.Context.BlockNumber) - p, found = precompiles[addr] - return p, found -} - -// ActivePrecompiles returns a list of all the active precompiled contract addresses -// for the current chain configuration. -func (EVM) ActivePrecompiles(rules params.Rules) []common.Address { - return vm.ActivePrecompiles(rules) -} - -// RunPrecompiledContract runs a stateless precompiled contract and ignores the address and -// value arguments. It uses the RunPrecompiledContract function from the geth vm package -func (EVM) RunPrecompiledContract( - p evm.StatefulPrecompiledContract, - _ common.Address, // address arg is unused - input []byte, - suppliedGas uint64, - _ *big.Int, // value arg is unused -) (ret []byte, remainingGas uint64, err error) { - return vm.RunPrecompiledContract(p, input, suppliedGas) -} diff --git a/x/evm/vm/geth/precompiles.go b/x/evm/vm/geth/precompiles.go deleted file mode 100644 index 66c842dfe2..0000000000 --- a/x/evm/vm/geth/precompiles.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 Evmos Foundation -// This file is part of Evmos' Ethermint library. -// -// The Ethermint library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Ethermint library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE -package geth - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" - - evm "github.com/evmos/ethermint/x/evm/vm" -) - -// GetPrecompiles returns all the precompiled contracts defined given the -// current chain configuration and block height. -func GetPrecompiles(cfg *params.ChainConfig, blockNumber *big.Int) evm.StatefulPrecompiledContracts { - var precompiles evm.StatefulPrecompiledContracts - switch { - case cfg.IsBerlin(blockNumber): - precompiles = vm.PrecompiledContractsBerlin - case cfg.IsIstanbul(blockNumber): - precompiles = vm.PrecompiledContractsIstanbul - case cfg.IsByzantium(blockNumber): - precompiles = vm.PrecompiledContractsByzantium - default: - precompiles = vm.PrecompiledContractsHomestead - } - return precompiles -} diff --git a/x/evm/vm/interface.go b/x/evm/vm/interface.go deleted file mode 100644 index c7eea40c76..0000000000 --- a/x/evm/vm/interface.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2021 Evmos Foundation -// This file is part of Evmos' Ethermint library. -// -// The Ethermint library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Ethermint library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE -package vm - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/precompile/contract" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" - "github.com/holiman/uint256" -) - -// PrecompiledContracts defines a map of address -> precompiled contract -type PrecompiledContracts map[common.Address]vm.PrecompiledContract - -type StatefulPrecompiledContracts map[common.Address]contract.StatefulPrecompiledContract - -type StatefulPrecompiledContract interface { - vm.PrecompiledContract - RunStateful(evm EVM, addr common.Address, input []byte, value *big.Int) (ret []byte, err error) -} - -// EVM defines the interface for the Ethereum Virtual Machine used by the EVM module. -type EVM interface { - Config() vm.Config - Context() vm.BlockContext - TxContext() vm.TxContext - - Reset(txCtx vm.TxContext, statedb vm.StateDB) - Cancel() - Cancelled() bool //nolint - Interpreter() *vm.EVMInterpreter - Call(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) - CallCode(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) - DelegateCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) - StaticCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) - Create(caller vm.ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) - Create2( - caller vm.ContractRef, - code []byte, - gas uint64, - endowment *big.Int, - salt *uint256.Int) ( - ret []byte, contractAddr common.Address, leftOverGas uint64, err error, - ) - ChainConfig() *params.ChainConfig - - ActivePrecompiles(rules params.Rules) []common.Address - Precompile(addr common.Address) (contract.StatefulPrecompiledContract, bool) - RunPrecompiledContract( - p StatefulPrecompiledContract, - addr common.Address, - input []byte, - suppliedGas uint64, - value *big.Int) ( - ret []byte, remainingGas uint64, err error, - ) -} - -// Constructor defines the function used to instantiate the EVM on -// each state transition. -type Constructor func( - blockCtx vm.BlockContext, - txCtx vm.TxContext, - stateDB vm.StateDB, - chainConfig *params.ChainConfig, - config vm.Config, - customPrecompiles PrecompiledContracts, -) EVM From 05a3a9052a7d8d6da5b3eea4133a6ec6aa80a32b Mon Sep 17 00:00:00 2001 From: evgeniy-scherbina Date: Thu, 18 Apr 2024 16:10:59 -0400 Subject: [PATCH 2/2] Fix unit tests --- x/evm/keeper/params_test.go | 14 +++++++------- x/evm/migrations/v3/store_test.go | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/x/evm/keeper/params_test.go b/x/evm/keeper/params_test.go index af64009aab..38fc687f10 100644 --- a/x/evm/keeper/params_test.go +++ b/x/evm/keeper/params_test.go @@ -8,13 +8,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/evmos/ethermint/app" "github.com/evmos/ethermint/encoding" "github.com/evmos/ethermint/x/evm/keeper" "github.com/evmos/ethermint/x/evm/types" legacytypes "github.com/evmos/ethermint/x/evm/types/legacy" legacytestutil "github.com/evmos/ethermint/x/evm/types/legacy/testutil" - "github.com/evmos/ethermint/x/evm/vm/geth" ) func (suite *KeeperTestSuite) TestParams() { @@ -150,8 +150,8 @@ func (suite *KeeperTestSuite) TestLegacyParamsKeyTableRegistration() { return keeper.NewKeeper( cdc, storeKey, tKey, authtypes.NewModuleAddress("gov"), ak, - nil, nil, nil, nil, // OK to pass nil in for these since we only instantiate and use params - geth.NewEVM, + nil, nil, nil, // OK to pass nil in for these since we only instantiate and use params + vm.NewEVM, "", unregisteredSubspace, ) @@ -207,8 +207,8 @@ func (suite *KeeperTestSuite) TestRenamedFieldReturnsProperValueForLegacyParams( k := keeper.NewKeeper( cdc, storeKey, tKey, authtypes.NewModuleAddress("gov"), ak, - nil, nil, nil, nil, - geth.NewEVM, + nil, nil, nil, + vm.NewEVM, "", subspace, ) @@ -239,8 +239,8 @@ func (suite *KeeperTestSuite) TestNilLegacyParamsDoNotPanic() { k := keeper.NewKeeper( cdc, storeKey, tKey, authtypes.NewModuleAddress("gov"), ak, - nil, nil, nil, nil, // OK to pass nil in for these since we only instantiate and use params - geth.NewEVM, + nil, nil, nil, // OK to pass nil in for these since we only instantiate and use params + vm.NewEVM, "", subspace, ) diff --git a/x/evm/migrations/v3/store_test.go b/x/evm/migrations/v3/store_test.go index a01916a6f7..974e41ee0a 100644 --- a/x/evm/migrations/v3/store_test.go +++ b/x/evm/migrations/v3/store_test.go @@ -3,9 +3,9 @@ package v3_test import ( "testing" + "github.com/ethereum/go-ethereum/core/vm" "github.com/evmos/ethermint/x/evm/keeper" "github.com/evmos/ethermint/x/evm/types" - "github.com/evmos/ethermint/x/evm/vm/geth" "github.com/stretchr/testify/require" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -149,8 +149,8 @@ func TestKeyTableCompatiabilityWithKeeper(t *testing.T) { keeper.NewKeeper( cdc, storeKey, tKey, authtypes.NewModuleAddress("gov"), ak, - nil, nil, nil, nil, - geth.NewEVM, + nil, nil, nil, + vm.NewEVM, "", subspace, )