Skip to content

Commit

Permalink
add precompile
Browse files Browse the repository at this point in the history
  • Loading branch information
mmsqe committed Sep 6, 2023
1 parent 05e6989 commit ce4a2af
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 64 deletions.
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
4 changes: 2 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 @@ -465,7 +464,8 @@ 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,
app.IBCKeeper, tracer,
evmSs, nil,
)

// 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
24 changes: 11 additions & 13 deletions x/evm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"

ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper"
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 +45,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 @@ -62,6 +62,8 @@ type Keeper struct {
// fetch EIP1559 base fee and parameters
feeMarketKeeper types.FeeMarketKeeper

ibcKeeper *ibckeeper.Keeper

// chain ID number obtained from the context's chain id
eip155ChainID *big.Int

Expand All @@ -71,13 +73,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
}

// NewKeeper generates new evm module keeper
Expand All @@ -89,10 +87,10 @@ func NewKeeper(
bankKeeper types.BankKeeper,
sk types.StakingKeeper,
fmk types.FeeMarketKeeper,
customPrecompiles evm.PrecompiledContracts,
evmConstructor evm.Constructor,
ik *ibckeeper.Keeper,
tracer string,
ss paramstypes.Subspace,
customContractsFn func(ctx sdk.Context, stateDB vm.StateDB) []precompiles.StatefulPrecompiledContract,
) *Keeper {
// ensure evm module account is set
if addr := ak.GetModuleAddress(types.ModuleName); addr == nil {
Expand All @@ -112,12 +110,12 @@ func NewKeeper(
bankKeeper: bankKeeper,
stakingKeeper: sk,
feeMarketKeeper: fmk,
ibcKeeper: ik,
storeKey: storeKey,
transientKey: transientKey,
customPrecompiles: customPrecompiles,
evmConstructor: evmConstructor,
tracer: tracer,
ss: ss,
customContractsFn: customContractsFn,
}
}

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 @@ func (k *Keeper) NewEVM(
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 @@ func (k *Keeper) NewEVM(
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)
}

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
})
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 @@ func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context,
}

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 @@ 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.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 @@ -18,19 +18,8 @@ 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
Expand Down
20 changes: 20 additions & 0 deletions x/evm/statedb/native.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package statedb

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

var _ JournalEntry = nativeChange{}

type nativeChange struct {
snapshot sdk.CacheMultiStore
}

func (native nativeChange) Dirtied() *common.Address {
return nil
}

func (native nativeChange) Revert(s *StateDB) {
s.restoreNativeState(native.snapshot)
}
Loading

0 comments on commit ce4a2af

Please sign in to comment.