From 8737d1063bd99b198282f342f71d934de5a08bce Mon Sep 17 00:00:00 2001 From: evgeniy-scherbina Date: Thu, 15 Feb 2024 14:38:00 -0500 Subject: [PATCH] Initial implementation of IBC Precompile (branch forked from yevhenii/precompile-v2) --- app/app.go | 2 +- go.sum | 2 +- x/evm/keeper/keeper.go | 8 +++++ x/evm/keeper/state_transition.go | 4 ++- x/evm/statedb/interfaces.go | 9 ++++++ x/evm/statedb/statedb.go | 51 ++++++++++++++++++++++++++++++++ x/evm/vm/geth/geth.go | 4 ++- x/evm/vm/interface.go | 2 ++ 8 files changed, 78 insertions(+), 4 deletions(-) diff --git a/app/app.go b/app/app.go index e828c0e11f..7ab3481c76 100644 --- a/app/app.go +++ b/app/app.go @@ -421,7 +421,7 @@ 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, + app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.FeeMarketKeeper, nil, nil, geth.NewEVM, tracer, evmSs, ) diff --git a/go.sum b/go.sum index f902387280..4a8834ca44 100644 --- a/go.sum +++ b/go.sum @@ -901,6 +901,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= @@ -978,7 +979,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 9c47491d8d..eb1bce4157 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -63,6 +63,8 @@ type Keeper struct { // fetch EIP1559 base fee and parameters feeMarketKeeper types.FeeMarketKeeper + ibcTransferKeeper statedb.IBCTransferKeeper + // chain ID number obtained from the context's chain id eip155ChainID *big.Int @@ -90,6 +92,7 @@ func NewKeeper( bankKeeper types.BankKeeper, sk types.StakingKeeper, fmk types.FeeMarketKeeper, + ibcTransferKeeper statedb.IBCTransferKeeper, customPrecompiles evm.PrecompiledContracts, evmConstructor evm.Constructor, tracer string, @@ -117,6 +120,7 @@ func NewKeeper( bankKeeper: bankKeeper, stakingKeeper: sk, feeMarketKeeper: fmk, + ibcTransferKeeper: ibcTransferKeeper, storeKey: storeKey, transientKey: transientKey, customPrecompiles: customPrecompiles, @@ -398,3 +402,7 @@ func (k Keeper) AddTransientGasUsed(ctx sdk.Context, gasUsed uint64) (uint64, er k.SetTransientGasUsed(ctx, result) return result, nil } + +func (k Keeper) IBCTransferKeeper() statedb.IBCTransferKeeper { + return k.ibcTransferKeeper +} diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index ad90dba5fd..d4b80f008d 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -52,6 +52,8 @@ func (k *Keeper) NewEVM( tracer vm.EVMLogger, stateDB vm.StateDB, ) evm.EVM { + goCtx := sdk.WrapSDKContext(ctx) + blockCtx := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -70,7 +72,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(goCtx, blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig, k.customPrecompiles) } // GetHashFn implements vm.GetHashFunc for Ethermint. It handles 3 cases: diff --git a/x/evm/statedb/interfaces.go b/x/evm/statedb/interfaces.go index e4e83e09c3..ec5f27e8cb 100644 --- a/x/evm/statedb/interfaces.go +++ b/x/evm/statedb/interfaces.go @@ -16,7 +16,10 @@ package statedb import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" + ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" ) @@ -31,6 +34,10 @@ type ExtStateDB interface { AppendJournalEntry(JournalEntry) } +type IBCTransferKeeper interface { + Transfer(goCtx context.Context, msg *ibctransfertypes.MsgTransfer) (*ibctransfertypes.MsgTransferResponse, error) +} + // Keeper provide underlying storage of StateDB type Keeper interface { // Read methods @@ -45,4 +52,6 @@ type Keeper interface { SetState(ctx sdk.Context, addr common.Address, key common.Hash, value []byte) SetCode(ctx sdk.Context, codeHash []byte, code []byte) DeleteAccount(ctx sdk.Context, addr common.Address) error + + IBCTransferKeeper() IBCTransferKeeper } diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 753ec76164..2201fee00b 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -16,16 +16,21 @@ package statedb import ( + "context" "fmt" "math/big" "sort" errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" + ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" + ibcclienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" "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" + "github.com/ethereum/go-ethereum/precompile/contract" ) // revision is the identifier of a version of state. @@ -85,6 +90,10 @@ func (s *StateDB) Keeper() Keeper { return s.keeper } +func (s *StateDB) Context() context.Context { + return s.ctx +} + // AddLog adds a log, called by evm. func (s *StateDB) AddLog(log *ethtypes.Log) { s.journal.append(addLogChange{}) @@ -476,3 +485,45 @@ func (s *StateDB) Commit() error { } return nil } + +func (s *StateDB) IBCTransfer(goCtx context.Context, msg *contract.MsgTransfer) (*contract.MsgTransferResponse, error) { + resp, err := s.keeper.IBCTransferKeeper().Transfer(goCtx, newMsgTransfer(msg)) + if err != nil { + return nil, err + } + + return newMsgTransferResponse(resp), nil +} + +func newMsgTransfer(m *contract.MsgTransfer) *ibctransfertypes.MsgTransfer { + return &ibctransfertypes.MsgTransfer{ + SourcePort: m.SourcePort, + SourceChannel: m.SourceChannel, + Token: newCoin(m.Token), + Sender: m.Sender, + Receiver: m.Receiver, + TimeoutHeight: newHeight(m.TimeoutHeight), + TimeoutTimestamp: m.TimeoutTimestamp, + Memo: m.Memo, + } +} + +func newCoin(c contract.Coin) types.Coin { + return types.Coin{ + Denom: c.Denom, + Amount: c.Amount, + } +} + +func newHeight(h contract.Height) ibcclienttypes.Height { + return ibcclienttypes.Height{ + RevisionNumber: h.RevisionNumber, + RevisionHeight: h.RevisionHeight, + } +} + +func newMsgTransferResponse(r *ibctransfertypes.MsgTransferResponse) *contract.MsgTransferResponse { + return &contract.MsgTransferResponse{ + Sequence: r.Sequence, + } +} diff --git a/x/evm/vm/geth/geth.go b/x/evm/vm/geth/geth.go index ebbf8743db..0ee33c9658 100644 --- a/x/evm/vm/geth/geth.go +++ b/x/evm/vm/geth/geth.go @@ -16,6 +16,7 @@ package geth import ( + "context" "math/big" "github.com/ethereum/go-ethereum/common" @@ -40,6 +41,7 @@ type EVM struct { // the default precompiled contracts and the EVM concrete implementation from // geth. func NewEVM( + goCtx context.Context, blockCtx vm.BlockContext, txCtx vm.TxContext, stateDB vm.StateDB, @@ -48,7 +50,7 @@ func NewEVM( _ evm.PrecompiledContracts, // unused ) evm.EVM { return &EVM{ - EVM: vm.NewEVM(blockCtx, txCtx, stateDB, chainConfig, config), + EVM: vm.NewEVMWithContext(goCtx, blockCtx, txCtx, stateDB, chainConfig, config), } } diff --git a/x/evm/vm/interface.go b/x/evm/vm/interface.go index ef88ffb38e..b96dcd14d0 100644 --- a/x/evm/vm/interface.go +++ b/x/evm/vm/interface.go @@ -16,6 +16,7 @@ package vm import ( + "context" "math/big" "github.com/ethereum/go-ethereum/common" @@ -75,6 +76,7 @@ type EVM interface { // Constructor defines the function used to instantiate the EVM on // each state transition. type Constructor func( + goCtx context.Context, blockCtx vm.BlockContext, txCtx vm.TxContext, stateDB vm.StateDB,