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

feat: precompiled contract poc #19

Merged
merged 16 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 11 additions & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,16 @@ 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,
appCodec,
keys[evmtypes.StoreKey],
tkeys[evmtypes.TransientKey],
app.GetSubspace(evmtypes.ModuleName),
app.AccountKeeper,
app.BankKeeper,
app.StakingKeeper,
app.FeeMarketKeeper,
tracer,
nil,
)

// Create IBC Keeper
Expand Down Expand Up @@ -646,6 +653,8 @@ func (app *EthermintApp) BlockedAddrs() map[string]bool {
blockedAddrs[authtypes.NewModuleAddress(acc).String()] = !allowedReceivingModAcc[acc]
}

// TODO(dudong2): need to add precompiled contract address?

return blockedAddrs
}

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ require (
replace (
github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76
github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0
github.com/cosmos/cosmos-sdk => github.com/b-harvest/cosmos-sdk v0.0.0-20240911104149-d2486fbebf5f
github.com/ethereum/go-ethereum => github.com/b-harvest/go-ethereum v0.0.0-20240911104320-ceb46abda914
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.34.33
google.golang.org/grpc => google.golang.org/grpc v1.33.2
Expand Down
60 changes: 16 additions & 44 deletions go.sum

Large diffs are not rendered by default.

36 changes: 24 additions & 12 deletions x/evm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,28 @@ package keeper
import (
"math/big"

"github.com/tendermint/tendermint/libs/log"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"

"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/params"
"github.com/tendermint/tendermint/libs/log"

ethermint "github.com/evmos/ethermint/types"
"github.com/evmos/ethermint/x/evm/statedb"
"github.com/evmos/ethermint/x/evm/types"
)

// CustomContractFn defines a custom precompiled contract generator with ctx, rules and returns a precompiled contract.
type CustomContractFn func(sdk.Context, params.Rules) vm.PrecompiledContract

// Keeper grants access to the EVM module state and implements the go-ethereum StateDB interface.
type Keeper struct {
// Protobuf codec
Expand Down Expand Up @@ -53,15 +58,21 @@ type Keeper struct {

// EVM Hooks for tx post-processing
hooks types.EvmHooks

customContractFns []CustomContractFn
}

// NewKeeper generates new evm module keeper
func NewKeeper(
cdc codec.BinaryCodec,
storeKey, transientKey sdk.StoreKey, paramSpace paramtypes.Subspace,
ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper,
storeKey, transientKey sdk.StoreKey,
paramSpace paramtypes.Subspace,
ak types.AccountKeeper,
bankKeeper types.BankKeeper,
sk types.StakingKeeper,
fmk types.FeeMarketKeeper,
tracer string,
customContractFns []CustomContractFn,
) *Keeper {
// ensure evm module account is set
if addr := ak.GetModuleAddress(types.ModuleName); addr == nil {
Expand All @@ -75,15 +86,16 @@ 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,
paramSpace: paramSpace,
accountKeeper: ak,
bankKeeper: bankKeeper,
stakingKeeper: sk,
feeMarketKeeper: fmk,
storeKey: storeKey,
transientKey: transientKey,
tracer: tracer,
cdc: cdc,
paramSpace: paramSpace,
accountKeeper: ak,
bankKeeper: bankKeeper,
stakingKeeper: sk,
feeMarketKeeper: fmk,
storeKey: storeKey,
transientKey: transientKey,
tracer: tracer,
customContractFns: customContractFns,
}
}

Expand Down
26 changes: 24 additions & 2 deletions x/evm/keeper/state_transition.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package keeper

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

tmtypes "github.com/tendermint/tendermint/types"

Expand Down Expand Up @@ -93,7 +95,27 @@ 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)

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)
}
for _, fn := range k.customContractFns {
c := fn(ctx, rules)
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
}

// VMConfig creates an EVM configuration from the debug setting and the extra EIPs enabled on the
Expand Down Expand Up @@ -392,7 +414,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 {
Expand Down
14 changes: 14 additions & 0 deletions x/evm/statedb/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"sort"

"github.com/ethereum/go-ethereum/common"

sdk "github.com/cosmos/cosmos-sdk/types"
)

// journalEntry is a modification entry in the state change journal that can be
Expand Down Expand Up @@ -139,6 +141,10 @@ type (
address *common.Address
slot *common.Hash
}
precompileChange struct {
ms sdk.MultiStore
Copy link
Collaborator

@cloudgray cloudgray Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Question] Is there any reason why multiStore was changed to ms and events to es?
[Suggestion] How abount snapshot and events? You can ignore this.

es sdk.Events
}
)

func (ch createObjectChange) revert(s *StateDB) {
Expand Down Expand Up @@ -241,3 +247,11 @@ func (ch accessListAddSlotChange) revert(s *StateDB) {
func (ch accessListAddSlotChange) dirtied() *common.Address {
return nil
}

func (ch precompileChange) revert(s *StateDB) {
s.RevertWithMultiStoreSnapshot(ch.ms)
}

func (ch precompileChange) dirtied() *common.Address {
return nil
}
63 changes: 63 additions & 0 deletions x/evm/statedb/statedb.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package statedb

import (
"errors"
"fmt"
"math/big"
"sort"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
)

type EventConverter = func(sdk.Event) (*ethtypes.Log, error)

// revision is the identifier of a version of state.
// it consists of an auto-increment id and a journal index.
// it's safer to use than using journal index alone.
Expand All @@ -32,6 +36,9 @@ type StateDB struct {
keeper Keeper
ctx sdk.Context

cacheCtx sdk.Context
writeCache func()

// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal *journal
Expand Down Expand Up @@ -283,6 +290,57 @@ func (s *StateDB) setStateObject(object *stateObject) {
s.stateObjects[object.Address()] = object
}

func (s *StateDB) GetCacheContext() (sdk.Context, error) {
if s.writeCache == nil {
if s.ctx.MultiStore() == nil {
return s.ctx, errors.New("ctx has no multi store")
}
s.cacheCtx, s.writeCache = s.ctx.CacheContext()
}

return s.cacheCtx, nil
}

func (s *StateDB) MultiStoreSnapshot() (sdk.CacheMultiStore, error) {
ctx, err := s.GetCacheContext()
if err != nil { // means s.ctx.MultiStore() == nil
return nil, err
}

cms := ctx.MultiStore().(sdk.CacheMultiStore)
snapshot := cms.Copy()
return snapshot, nil
}

func (s *StateDB) RevertWithMultiStoreSnapshot(snapshot sdk.MultiStore) {
s.cacheCtx = s.cacheCtx.
WithMultiStore(snapshot).
WithEventManager(sdk.NewEventManager())
}

// If revert is occurred, the snapshot of journal is overwrited to MultiStore of ctx.
// The events is just for debug.
func (s *StateDB) PostPrecompileProcessing(snapshot sdk.MultiStore, events sdk.Events, contract common.Address, converter EventConverter) {
// convert native events to evm logs
if converter != nil && len(events) > 0 {
for _, event := range events {
log, err := converter(event)
if err != nil {
s.ctx.Logger().Error("failed to convert event", "err", err)
continue
}
if log == nil {
continue
}

log.Address = contract
s.AddLog(log)
}
}

s.journal.append(precompileChange{snapshot, events})
}

/*
* SETTERS
*/
Expand Down Expand Up @@ -436,6 +494,11 @@ func (s *StateDB) RevertToSnapshot(revid int) {
// Commit writes the dirty states to keeper
// the StateDB object should be discarded after committed.
func (s *StateDB) Commit() error {
// write all store updates from precompile
if s.writeCache != nil {
s.writeCache()
}

for _, addr := range s.journal.sortedDirties() {
obj := s.stateObjects[addr]
if obj.suicided {
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 @@ -30,7 +30,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