Sync from fork #74

Merged
0xmuralik merged 232 commits from murali/update-fork into main 2023-01-10 04:50:57 +00:00
12 changed files with 261 additions and 58 deletions
Showing only changes of commit acf15474e7 - Show all commits

View File

@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### State Machine Breaking
* (evm) [\#1272](https://github.com/evmos/ethermint/pull/1272) Implement modular interface for the EVM.
* (deps) [\#1159](https://github.com/evmos/ethermint/pull/1159) Bump Geth version to `v1.10.19`.
* (deps) [#1167](https://github.com/evmos/ethermint/pull/1167) Bump ibc-go to [`v4.0.0-rc2`](https://github.com/cosmos/ibc-go/releases/tag/v4.0.0-rc2)
* (ante) [#1176](https://github.com/evmos/ethermint/pull/1176) Fix invalid tx hashes; Remove `Size_` field and validate `Hash`/`From` fields in ante handler,

View File

@ -305,7 +305,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, sdkerrors.Wrapf(
sdkerrors.ErrInsufficientFunds,
"failed to transfer %s from address %s using the EVM block context transfer function",

View File

@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"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"
)
@ -26,7 +27,7 @@ type EVMKeeper interface {
statedb.Keeper
DynamicFeeEVMKeeper
NewEVM(ctx sdk.Context, msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB) *vm.EVM
NewEVM(ctx sdk.Context, msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB) evm.EVM
DeductTxCostsFromUserBalance(
ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool,
) (fees sdk.Coins, priority int64, err error)

View File

@ -106,6 +106,7 @@ import (
"github.com/evmos/ethermint/x/evm"
evmkeeper "github.com/evmos/ethermint/x/evm/keeper"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"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"
@ -396,7 +397,7 @@ func NewEthermintApp(
app.EvmKeeper = evmkeeper.NewKeeper(
appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], app.GetSubspace(evmtypes.ModuleName),
app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.FeeMarketKeeper,
tracer,
nil, geth.NewEVM, tracer,
)
// Create IBC Keeper

View File

@ -19,6 +19,7 @@ import (
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"
)
// Keeper grants access to the EVM module state and implements the go-ethereum StateDB interface.
@ -54,14 +55,25 @@ 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
}
// NewKeeper generates new evm module keeper
func NewKeeper(
cdc codec.BinaryCodec,
storeKey, transientKey storetypes.StoreKey, paramSpace paramtypes.Subspace,
ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper,
storeKey, transientKey storetypes.StoreKey,
paramSpace paramtypes.Subspace,
ak types.AccountKeeper,
bankKeeper types.BankKeeper,
sk types.StakingKeeper,
fmk types.FeeMarketKeeper,
customPrecompiles evm.PrecompiledContracts,
evmConstructor evm.Constructor,
tracer string,
) *Keeper {
// ensure evm module account is set
@ -84,6 +96,8 @@ func NewKeeper(
feeMarketKeeper: fmk,
storeKey: storeKey,
transientKey: transientKey,
customPrecompiles: customPrecompiles,
evmConstructor: evmConstructor,
tracer: tracer,
}
}

View File

@ -16,6 +16,7 @@ import (
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"
@ -77,7 +78,7 @@ func (k *Keeper) NewEVM(
cfg *types.EVMConfig,
tracer vm.EVMLogger,
stateDB vm.StateDB,
) *vm.EVM {
) evm.EVM {
blockCtx := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
@ -95,7 +96,7 @@ func (k *Keeper) NewEVM(
tracer = k.Tracer(ctx, msg, cfg.ChainConfig)
}
vmConfig := k.VMConfig(ctx, msg, cfg, tracer)
return vm.NewEVM(blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig)
return k.evmConstructor(blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig, k.customPrecompiles)
}
// VMConfig creates an EVM configuration from the debug setting and the extra EIPs enabled on the
@ -366,17 +367,19 @@ func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context,
evm := k.NewEVM(ctx, msg, cfg, tracer, stateDB)
leftoverGas := msg.Gas()
// Allow the tracer captures the tx level events, mainly the gas consumption.
if evm.Config.Debug {
evm.Config.Tracer.CaptureTxStart(leftoverGas)
vmCfg := evm.Config()
if vmCfg.Debug {
vmCfg.Tracer.CaptureTxStart(leftoverGas)
defer func() {
evm.Config.Tracer.CaptureTxEnd(leftoverGas)
vmCfg.Tracer.CaptureTxEnd(leftoverGas)
}()
}
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 {
@ -394,7 +397,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(), vm.ActivePrecompiles(rules), msg.AccessList())
stateDB.PrepareAccessList(msg.From(), msg.To(), evm.ActivePrecompiles(rules), msg.AccessList())
}
if contractCreation {

View File

@ -3,8 +3,19 @@ package statedb
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)
// ExtStateDB defines an extension to the interface provided by the go-ethereum
// codebase to support additional state transition functionalities. In particular
// it supports appending a new entry to the state journal through
// AppendJournalEntry so that the state can be reverted after running
// stateful precompiled contracts.
type ExtStateDB interface {
vm.StateDB
AppendJournalEntry(JournalEntry)
}
// Keeper provide underlying storage of StateDB
type Keeper interface {
// Read methods

View File

@ -24,21 +24,21 @@ import (
"github.com/ethereum/go-ethereum/common"
)
// journalEntry is a modification entry in the state change journal that can be
// reverted on demand.
type journalEntry interface {
// revert undoes the changes introduced by this journal entry.
revert(*StateDB)
// JournalEntry is a modification entry in the state change journal that can be
// Reverted on demand.
type JournalEntry interface {
// Revert undoes the changes introduced by this journal entry.
Revert(*StateDB)
// dirtied returns the Ethereum address modified by this journal entry.
dirtied() *common.Address
// Dirtied returns the Ethereum address modified by this journal entry.
Dirtied() *common.Address
}
// journal contains the list of state modifications applied since the last state
// commit. These are tracked to be able to be reverted in the case of an execution
// exception or request for reversal.
type journal struct {
entries []journalEntry // Current changes tracked by the journal
entries []JournalEntry // Current changes tracked by the journal
dirties map[common.Address]int // Dirty accounts and the number of changes
}
@ -64,22 +64,22 @@ func (j *journal) sortedDirties() []common.Address {
}
// append inserts a new modification entry to the end of the change journal.
func (j *journal) append(entry journalEntry) {
func (j *journal) append(entry JournalEntry) {
j.entries = append(j.entries, entry)
if addr := entry.dirtied(); addr != nil {
if addr := entry.Dirtied(); addr != nil {
j.dirties[*addr]++
}
}
// revert undoes a batch of journalled modifications along with any reverted
// Revert undoes a batch of journalled modifications along with any Reverted
// dirty handling too.
func (j *journal) revert(statedb *StateDB, snapshot int) {
func (j *journal) Revert(statedb *StateDB, snapshot int) {
for i := len(j.entries) - 1; i >= snapshot; i-- {
// Undo the changes made by the operation
j.entries[i].revert(statedb)
j.entries[i].Revert(statedb)
// Drop any dirty tracking induced by the change
if addr := j.entries[i].dirtied(); addr != nil {
if addr := j.entries[i].Dirtied(); addr != nil {
if j.dirties[*addr]--; j.dirties[*addr] == 0 {
delete(j.dirties, *addr)
}
@ -141,23 +141,23 @@ type (
}
)
func (ch createObjectChange) revert(s *StateDB) {
func (ch createObjectChange) Revert(s *StateDB) {
delete(s.stateObjects, *ch.account)
}
func (ch createObjectChange) dirtied() *common.Address {
func (ch createObjectChange) Dirtied() *common.Address {
return ch.account
}
func (ch resetObjectChange) revert(s *StateDB) {
func (ch resetObjectChange) Revert(s *StateDB) {
s.setStateObject(ch.prev)
}
func (ch resetObjectChange) dirtied() *common.Address {
func (ch resetObjectChange) Dirtied() *common.Address {
return nil
}
func (ch suicideChange) revert(s *StateDB) {
func (ch suicideChange) Revert(s *StateDB) {
obj := s.getStateObject(*ch.account)
if obj != nil {
obj.suicided = ch.prev
@ -165,59 +165,59 @@ func (ch suicideChange) revert(s *StateDB) {
}
}
func (ch suicideChange) dirtied() *common.Address {
func (ch suicideChange) Dirtied() *common.Address {
return ch.account
}
func (ch balanceChange) revert(s *StateDB) {
func (ch balanceChange) Revert(s *StateDB) {
s.getStateObject(*ch.account).setBalance(ch.prev)
}
func (ch balanceChange) dirtied() *common.Address {
func (ch balanceChange) Dirtied() *common.Address {
return ch.account
}
func (ch nonceChange) revert(s *StateDB) {
func (ch nonceChange) Revert(s *StateDB) {
s.getStateObject(*ch.account).setNonce(ch.prev)
}
func (ch nonceChange) dirtied() *common.Address {
func (ch nonceChange) Dirtied() *common.Address {
return ch.account
}
func (ch codeChange) revert(s *StateDB) {
func (ch codeChange) Revert(s *StateDB) {
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
}
func (ch codeChange) dirtied() *common.Address {
func (ch codeChange) Dirtied() *common.Address {
return ch.account
}
func (ch storageChange) revert(s *StateDB) {
func (ch storageChange) Revert(s *StateDB) {
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
func (ch storageChange) dirtied() *common.Address {
func (ch storageChange) Dirtied() *common.Address {
return ch.account
}
func (ch refundChange) revert(s *StateDB) {
func (ch refundChange) Revert(s *StateDB) {
s.refund = ch.prev
}
func (ch refundChange) dirtied() *common.Address {
func (ch refundChange) Dirtied() *common.Address {
return nil
}
func (ch addLogChange) revert(s *StateDB) {
func (ch addLogChange) Revert(s *StateDB) {
s.logs = s.logs[:len(s.logs)-1]
}
func (ch addLogChange) dirtied() *common.Address {
func (ch addLogChange) Dirtied() *common.Address {
return nil
}
func (ch accessListAddAccountChange) revert(s *StateDB) {
func (ch accessListAddAccountChange) Revert(s *StateDB) {
/*
One important invariant here, is that whenever a (addr, slot) is added, if the
addr is not already present, the add causes two journal entries:
@ -230,14 +230,14 @@ func (ch accessListAddAccountChange) revert(s *StateDB) {
s.accessList.DeleteAddress(*ch.address)
}
func (ch accessListAddAccountChange) dirtied() *common.Address {
func (ch accessListAddAccountChange) Dirtied() *common.Address {
return nil
}
func (ch accessListAddSlotChange) revert(s *StateDB) {
func (ch accessListAddSlotChange) Revert(s *StateDB) {
s.accessList.DeleteSlot(*ch.address, *ch.slot)
}
func (ch accessListAddSlotChange) dirtied() *common.Address {
func (ch accessListAddSlotChange) Dirtied() *common.Address {
return nil
}

View File

@ -429,7 +429,7 @@ func (s *StateDB) RevertToSnapshot(revid int) {
snapshot := s.validRevisions[idx].journalIndex
// Replay the journal to undo changes and remove invalidated snapshots
s.journal.revert(s, snapshot)
s.journal.Revert(s, snapshot)
s.validRevisions = s.validRevisions[:idx]
}

79
x/evm/vm/geth/geth.go Normal file
View File

@ -0,0 +1,79 @@
package geth
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
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 vm.PrecompiledContract, 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)
}

View File

@ -0,0 +1,27 @@
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.PrecompiledContracts {
var precompiles evm.PrecompiledContracts
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
}

66
x/evm/vm/interface.go Normal file
View File

@ -0,0 +1,66 @@
package vm
import (
"math/big"
"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 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) (vm.PrecompiledContract, 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