Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem: precompile interface is not supported #328

Merged
merged 6 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

* (rpc) [#1682](https://github.com/evmos/ethermint/pull/1682) Add config for maximum number of bytes returned from eth_call.
* (ante) [#310](https://github.com/crypto-org-chain/ethermint/pull/310) Support blocking list of addresses in mempool.
* (evm) [#328](https://github.com/crypto-org-chain/ethermint/pull/328) Support precompile interface.
* (statedb) [#333](https://github.com/crypto-org-chain/ethermint/pull/333) Support native action in statedb, prepare for precompiles.

### State Machine Breaking
Expand Down
8 changes: 3 additions & 5 deletions app/ante/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,7 @@ type CanTransferDecorator struct {

// NewCanTransferDecorator creates a new CanTransferDecorator instance.
func NewCanTransferDecorator(evmKeeper EVMKeeper) CanTransferDecorator {
return CanTransferDecorator{
evmKeeper: evmKeeper,
}
return CanTransferDecorator{evmKeeper}
}

// AnteHandle creates an EVM from the message and calls the BlockContext CanTransfer function to
Expand Down Expand Up @@ -303,11 +301,11 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
}

stateDB := statedb.New(ctx, ctd.evmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(ctx.HeaderHash().Bytes())))
evm := ctd.evmKeeper.NewEVM(ctx, coreMsg, cfg, evmtypes.NewNoOpTracer(), stateDB)
evm := ctd.evmKeeper.NewEVM(ctx, coreMsg, cfg, evmtypes.NewNoOpTracer(), stateDB, nil)

// 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",
Expand Down
6 changes: 3 additions & 3 deletions app/ante/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"

"github.com/evmos/ethermint/x/evm/keeper/precompiles"
"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"
)

Expand All @@ -44,7 +43,8 @@ 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,
customContracts []precompiles.StatefulPrecompiledContract) *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)
Expand Down
5 changes: 3 additions & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ 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"
Expand Down Expand Up @@ -460,7 +459,9 @@ func NewEthermintApp(
app.EvmKeeper = evmkeeper.NewKeeper(
appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName),
app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.FeeMarketKeeper,
nil, geth.NewEVM, tracer, evmSs, keys,
tracer,
evmSs, nil,
keys,
)

// Create IBC Keeper
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ replace (
github.com/cometbft/cometbft => github.com/cometbft/cometbft v0.37.2-0.20230905115601-790d57e1748f
github.com/cometbft/cometbft-db => github.com/crypto-org-chain/cometbft-db v0.0.0-20230412133340-ac70df4b45f6
github.com/cosmos/cosmos-sdk => github.com/crypto-org-chain/cosmos-sdk v0.46.0-beta2.0.20230905040840-b3af5590283b
github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc1
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
// TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s=
github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
github.com/evmos/go-ethereum v1.10.26-evmos-rc1 h1:8+jrotZVyO0eIJGRGa1Kga3Fo7DjgFUE9rd6A8Yrbcg=
github.com/evmos/go-ethereum v1.10.26-evmos-rc1/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
Expand Down
5 changes: 3 additions & 2 deletions gomod2nix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,9 @@ schema = 3
version = "v1.0.0"
hash = "sha256-k1DYvCqO3BKNcGEve/nMW0RxzMkK2tGfXbUbycqcVSo="
[mod."github.com/ethereum/go-ethereum"]
version = "v1.10.26"
hash = "sha256-gkMEwJ4rOgn12amD4QpZ4th/10uyTTeoFmpseuKDQPs="
version = "v1.10.26-evmos-rc1"
hash = "sha256-GgcReGsIIuBE2TabDYqDO9sBGogdVr9RSh4arQzdPnE="
replaced = "github.com/evmos/go-ethereum"
[mod."github.com/felixge/httpsnoop"]
version = "v1.0.2"
hash = "sha256-hj6FZQ1fDAV+1wGIViAt8XaAkWZ1I5vJzgjIJa7XRBA="
Expand Down
17 changes: 7 additions & 10 deletions rpc/backend/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,20 +315,17 @@ func TxLogsFromEvents(events []abci.Event, msgIndex int) ([]*ethtypes.Log, error

// ParseTxLogsFromEvent parse tx logs from one event
func ParseTxLogsFromEvent(event abci.Event) ([]*ethtypes.Log, error) {
logs := make([]*evmtypes.Log, 0, len(event.Attributes))
var ethLogs []*ethtypes.Log
for _, attr := range event.Attributes {
if attr.Key != evmtypes.AttributeKeyTxLog {
continue
}

var log evmtypes.Log
if err := json.Unmarshal([]byte(attr.Value), &log); err != nil {
return nil, err
if attr.Key == evmtypes.AttributeKeyTxLog {
if err := json.Unmarshal([]byte(attr.Value), &log); err != nil {
return nil, err
}
ethLogs = append(ethLogs, log.ToEthereum())
}

logs = append(logs, &log)
}
return evmtypes.LogsToEthereum(logs), nil
return ethLogs, nil
}

// ShouldIgnoreGasUsed returns true if the gasUsed in result should be ignored
Expand Down
19 changes: 6 additions & 13 deletions x/evm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ import (
"github.com/ethereum/go-ethereum/params"

ethermint "github.com/evmos/ethermint/types"
"github.com/evmos/ethermint/x/evm/keeper/precompiles"
"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.
Expand All @@ -44,8 +44,7 @@ type Keeper struct {
// Store key required for the EVM Prefix KVStore. It is required by:
// - storing account's Storage State
// - storing account's Code
// - storing transaction Logs
// - storing Bloom filters by block height. Needed for the Web3 API.
// - storing module parameters
storeKey storetypes.StoreKey

// key to access the transient store, which is reset on every block during Commit
Expand All @@ -71,13 +70,9 @@ 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
// Legacy subspace
ss paramstypes.Subspace
ss paramstypes.Subspace
customContractsFn func(ctx sdk.Context, stateDB vm.StateDB) []precompiles.StatefulPrecompiledContract

// a set of store keys that should cover all the precompile use cases,
// or ideally just pass the application's all stores.
Expand All @@ -93,10 +88,9 @@ func NewKeeper(
bankKeeper types.BankKeeper,
sk types.StakingKeeper,
fmk types.FeeMarketKeeper,
customPrecompiles evm.PrecompiledContracts,
evmConstructor evm.Constructor,
tracer string,
ss paramstypes.Subspace,
customContractsFn func(ctx sdk.Context, stateDB vm.StateDB) []precompiles.StatefulPrecompiledContract,
keys map[string]*storetypes.KVStoreKey,
) *Keeper {
// ensure evm module account is set
Expand All @@ -119,10 +113,9 @@ func NewKeeper(
feeMarketKeeper: fmk,
storeKey: storeKey,
transientKey: transientKey,
customPrecompiles: customPrecompiles,
evmConstructor: evmConstructor,
tracer: tracer,
ss: ss,
customContractsFn: customContractsFn,
keys: keys,
}
}
Expand Down
16 changes: 16 additions & 0 deletions x/evm/keeper/precompiles/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package precompiles

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/core/vm"
)

type StatefulPrecompiledContract interface {
vm.PrecompiledContract
}

// ExtStateDB defines extra methods of statedb to support stateful precompiled contracts
type ExtStateDB interface {
vm.StateDB
ExecuteNativeAction(action func(ctx sdk.Context) error) error
}
44 changes: 32 additions & 12 deletions x/evm/keeper/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@
package keeper

import (
"bytes"
"math/big"
"sort"

tmtypes "github.com/cometbft/cometbft/types"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"

ethermint "github.com/evmos/ethermint/types"
"github.com/evmos/ethermint/x/evm/keeper/precompiles"
"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"
Expand All @@ -51,7 +53,8 @@
cfg *statedb.EVMConfig,
tracer vm.EVMLogger,
stateDB vm.StateDB,
) evm.EVM {
customContracts []precompiles.StatefulPrecompiledContract,
) *vm.EVM {
blockCtx := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Expand All @@ -64,13 +67,29 @@
BaseFee: cfg.BaseFee,
Random: nil, // not supported
}

txCtx := core.NewEVMTxContext(msg)
if tracer == nil {
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)
rules := cfg.ChainConfig.Rules(big.NewInt(ctx.BlockHeight()), cfg.ChainConfig.MergeNetsplitBlock != nil)
contracts := make(map[common.Address]vm.PrecompiledContract)
active := make([]common.Address, 0)
for addr, c := range vm.DefaultPrecompiles(rules) {
contracts[addr] = c
active = append(active, addr)
}
Comment on lines +78 to +81

Check failure

Code scanning / gosec

the value in the range statement should be _ unless copying a map: want: for key := range m Error

expected exactly 1 statement (either append, delete, or copying to another map) in a range with a map, got 2
for _, c := range customContracts {
addr := c.Address()
contracts[addr] = c
active = append(active, addr)
}
sort.SliceStable(active, func(i, j int) bool {
return bytes.Compare(active[i].Bytes(), active[j].Bytes()) < 0
})
mmsqe marked this conversation as resolved.
Show resolved Hide resolved
evm := vm.NewEVM(blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig)
evm.WithPrecompiles(contracts, active)
return evm
}

// GetHashFn implements vm.GetHashFunc for Ethermint. It handles 3 cases:
Expand Down Expand Up @@ -331,23 +350,24 @@
}

stateDB := statedb.New(ctx, k, txConfig)
evm := k.NewEVM(ctx, msg, cfg, tracer, stateDB)

var customContracts []precompiles.StatefulPrecompiledContract
if k.customContractsFn != nil {
customContracts = k.customContractsFn(ctx, stateDB)
}
evm := k.NewEVM(ctx, msg, cfg, tracer, stateDB, customContracts)
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() {
vmCfg.Tracer.CaptureTxEnd(leftoverGas)
}()
}

sender := vm.AccountRef(msg.From())
isLondon := cfg.ChainConfig.IsLondon(evm.Context.BlockNumber)
contractCreation := msg.To() == nil
isLondon := cfg.ChainConfig.IsLondon(evm.Context().BlockNumber)

sender := vm.AccountRef(msg.From())
intrinsicGas, err := k.GetEthIntrinsicGas(ctx, msg, cfg.ChainConfig, contractCreation)
if err != nil {
// should have already been checked on Ante Handler
Expand All @@ -364,7 +384,7 @@
// 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.DefaultActivePrecompiles(rules), msg.AccessList())
}

if contractCreation {
Expand Down
11 changes: 0 additions & 11 deletions x/evm/statedb/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,8 @@ import (
storetypes "github.com/cosmos/cosmos-sdk/store/types"
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 {
// for cache store wrapping
Expand Down
6 changes: 5 additions & 1 deletion x/evm/statedb/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,9 @@
}

func (s *StateDB) restoreNativeState(ms sdk.MultiStore) {
s.cacheCtx = s.cacheCtx.WithMultiStore(ms)
manager := sdk.NewEventManager()
s.cacheCtx = s.cacheCtx.WithMultiStore(ms).WithEventManager(manager)
mmsqe marked this conversation as resolved.
Show resolved Hide resolved

Check failure on line 316 in x/evm/statedb/statedb.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

File is not `gofumpt`-ed (gofumpt)
}

// ExecuteNativeAction executes native action in isolate,
Expand Down Expand Up @@ -484,6 +486,8 @@
// commit the native cache store first,
// the states managed by precompiles and the other part of StateDB must not overlap.
s.CacheMultiStore().Write()
manager := s.cacheCtx.EventManager()
manager.EmitEvents(manager.Events())
mmsqe marked this conversation as resolved.
Show resolved Hide resolved

for _, addr := range s.journal.sortedDirties() {
obj := s.stateObjects[addr]
Expand Down
2 changes: 2 additions & 0 deletions x/evm/types/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type BankKeeper interface {
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error
BlockedAddr(addr sdk.AccAddress) bool
}

// StakingKeeper returns the historical headers kept in store.
Expand Down
2 changes: 1 addition & 1 deletion x/evm/types/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func NewTracer(tracer string, msg core.Message, cfg *params.ChainConfig, height

switch tracer {
case TracerAccessList:
preCompiles := vm.ActivePrecompiles(cfg.Rules(big.NewInt(height), cfg.MergeNetsplitBlock != nil))
preCompiles := vm.DefaultActivePrecompiles(cfg.Rules(big.NewInt(height), cfg.MergeNetsplitBlock != nil))
return logger.NewAccessListTracer(msg.AccessList(), msg.From(), *msg.To(), preCompiles)
case TracerJSON:
return logger.NewJSONLogger(logCfg, os.Stderr)
Expand Down
Loading
Loading