From add06b28237b02d90917807afd202ae560c2ddb4 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Sat, 8 Aug 2026 21:25:59 +0100 Subject: [PATCH 1/4] Retire all versions of IBC precompiles and remove legacy implementations Retire the IBC precompile at every version while preserving each historical ABI and the registered address. All valid calls now revert with a clear retirement reason. Remove obsolete legacy IBC implementations and unused keeper dependencies. Teach the version generator to skip retired modules, preventing IBC from being archived or reactivated during future upgrades. Add coverage confirming every registered IBC version reverts and remains non-payable. --- app/app.go | 4 +- app/precompiles.go | 12 - giga/deps/xevm/keeper/keeper.go | 5 +- .../common/legacy/v605/expected_keepers.go | 22 - .../common/legacy/v606/expected_keepers.go | 22 - .../common/legacy/v610/expected_keepers.go | 22 - precompiles/common/retired.go | 54 ++ precompiles/ibc/ibc.go | 396 +-------- precompiles/ibc/ibc_test.go | 781 ++---------------- precompiles/ibc/legacy/v552/ibc.go | 450 ---------- precompiles/ibc/legacy/v555/ibc.go | 481 ----------- precompiles/ibc/legacy/v562/ibc.go | 420 ---------- precompiles/ibc/legacy/v580/ibc.go | 406 --------- precompiles/ibc/legacy/v601/ibc.go | 406 --------- precompiles/ibc/legacy/v603/ibc.go | 397 --------- precompiles/ibc/legacy/v605/ibc.go | 401 --------- precompiles/ibc/legacy/v606/ibc.go | 401 --------- precompiles/ibc/legacy/v610/ibc.go | 401 --------- precompiles/ibc/legacy/v614/ibc.go | 401 --------- precompiles/ibc/legacy/v620/ibc.go | 401 --------- precompiles/ibc/legacy/v630/ibc.go | 401 --------- precompiles/ibc/legacy/v640/ibc.go | 401 --------- precompiles/ibc/legacy/v65/ibc.go | 401 --------- precompiles/ibc/legacy/v66/ibc.go | 403 --------- precompiles/ibc/retired | 1 + precompiles/ibc/setup.go | 71 +- precompiles/utils/expected_keepers.go | 41 - scripts/bump_version/README.md | 4 + scripts/bump_version/main.go | 11 +- tools/utils/helper.go | 8 +- x/evm/keeper/keeper.go | 5 +- 31 files changed, 195 insertions(+), 7435 deletions(-) create mode 100644 precompiles/common/retired.go delete mode 100644 precompiles/ibc/legacy/v552/ibc.go delete mode 100644 precompiles/ibc/legacy/v555/ibc.go delete mode 100644 precompiles/ibc/legacy/v562/ibc.go delete mode 100644 precompiles/ibc/legacy/v580/ibc.go delete mode 100644 precompiles/ibc/legacy/v601/ibc.go delete mode 100644 precompiles/ibc/legacy/v603/ibc.go delete mode 100644 precompiles/ibc/legacy/v605/ibc.go delete mode 100644 precompiles/ibc/legacy/v606/ibc.go delete mode 100644 precompiles/ibc/legacy/v610/ibc.go delete mode 100644 precompiles/ibc/legacy/v614/ibc.go delete mode 100644 precompiles/ibc/legacy/v620/ibc.go delete mode 100644 precompiles/ibc/legacy/v630/ibc.go delete mode 100644 precompiles/ibc/legacy/v640/ibc.go delete mode 100644 precompiles/ibc/legacy/v65/ibc.go delete mode 100644 precompiles/ibc/legacy/v66/ibc.go create mode 100644 precompiles/ibc/retired diff --git a/app/app.go b/app/app.go index ecab0bc8f1..77503eb42d 100644 --- a/app/app.go +++ b/app/app.go @@ -726,7 +726,7 @@ func New( } app.EvmKeeper = *evmkeeper.NewKeeper(keys[evmtypes.StoreKey], tkeys[evmtypes.TransientStoreKey], app.GetSubspace(evmtypes.ModuleName), app.receiptStore, app.BankKeeper, - &app.AccountKeeper, &app.StakingKeeper, app.TransferKeeper, + &app.AccountKeeper, &app.StakingKeeper, wasmkeeper.NewDefaultPermissionKeeper(app.WasmKeeper), &app.WasmKeeper, &app.UpgradeKeeper) app.BankKeeper.RegisterRecipientChecker(app.EvmKeeper.CanAddressReceive) @@ -797,7 +797,7 @@ func New( app.GigaEvmKeeper = *gigaevmkeeper.NewKeeper(keys[evmtypes.StoreKey], tkeys[evmtypes.TransientStoreKey], app.GetSubspace(evmtypes.ModuleName), app.receiptStore, app.GigaBankKeeper, - &app.AccountKeeper, &app.StakingKeeper, app.TransferKeeper, + &app.AccountKeeper, &app.StakingKeeper, wasmkeeper.NewDefaultPermissionKeeper(app.WasmKeeper), &app.WasmKeeper, &app.UpgradeKeeper) app.GigaEvmKeeper.UseRegularStore = true app.GigaBankKeeper.UseRegularStore = true diff --git a/app/precompiles.go b/app/precompiles.go index df70e31806..ee2c4eb0c3 100644 --- a/app/precompiles.go +++ b/app/precompiles.go @@ -38,10 +38,6 @@ type PrecompileKeepers struct { putils.SlashingMsgServer putils.SlashingQuerier putils.UpgradeQuerier - putils.TransferKeeper - putils.ClientKeeper - putils.ConnectionKeeper - putils.ChannelKeeper txConf client.TxConfig cdc codec.Codec } @@ -73,10 +69,6 @@ func NewPrecompileKeepers(a *App) *PrecompileKeepers { SlashingMsgServer: slashingkeeper.NewMsgServerImpl(a.SlashingKeeper), SlashingQuerier: a.SlashingKeeper, UpgradeQuerier: a.UpgradeKeeper, - TransferKeeper: a.TransferKeeper, - ClientKeeper: a.IBCKeeper.ClientKeeper, - ConnectionKeeper: a.IBCKeeper.ConnectionKeeper, - ChannelKeeper: a.IBCKeeper.ChannelKeeper, txConf: a.GetTxConfig(), cdc: a.appCodec, } @@ -109,9 +101,5 @@ func (pk *PrecompileKeepers) ParamsQ() putils.ParamsQuerier { return pk.P func (pk *PrecompileKeepers) SlashingMS() putils.SlashingMsgServer { return pk.SlashingMsgServer } func (pk *PrecompileKeepers) SlashingQ() putils.SlashingQuerier { return pk.SlashingQuerier } func (pk *PrecompileKeepers) UpgradeQ() putils.UpgradeQuerier { return pk.UpgradeQuerier } -func (pk *PrecompileKeepers) TransferK() putils.TransferKeeper { return pk.TransferKeeper } -func (pk *PrecompileKeepers) ClientK() putils.ClientKeeper { return pk.ClientKeeper } -func (pk *PrecompileKeepers) ConnectionK() putils.ConnectionKeeper { return pk.ConnectionKeeper } -func (pk *PrecompileKeepers) ChannelK() putils.ChannelKeeper { return pk.ChannelKeeper } func (pk *PrecompileKeepers) TxConfig() client.TxConfig { return pk.txConf } func (pk *PrecompileKeepers) Codec() codec.Codec { return pk.cdc } diff --git a/giga/deps/xevm/keeper/keeper.go b/giga/deps/xevm/keeper/keeper.go index 54813f5f35..af8a77d05f 100644 --- a/giga/deps/xevm/keeper/keeper.go +++ b/giga/deps/xevm/keeper/keeper.go @@ -33,7 +33,6 @@ import ( stakingkeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/staking/keeper" upgradekeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/upgrade/keeper" "github.com/sei-protocol/sei-chain/sei-db/ledger_db/receipt" - ibctransferkeeper "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/keeper" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" wasmkeeper "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/keeper" @@ -55,7 +54,6 @@ type Keeper struct { bankKeeper bankkeeper.Keeper accountKeeper *authkeeper.AccountKeeper stakingKeeper *stakingkeeper.Keeper - transferKeeper ibctransferkeeper.Keeper wasmKeeper *wasmkeeper.PermissionedKeeper wasmViewKeeper *wasmkeeper.Keeper upgradeKeeper *upgradekeeper.Keeper @@ -128,7 +126,7 @@ func (ctx *ReplayChainContext) Config() *params.ChainConfig { func NewKeeper( storeKey sdk.StoreKey, transientStoreKey sdk.StoreKey, paramstore paramtypes.Subspace, receiptStateStore receipt.ReceiptStore, bankKeeper bankkeeper.Keeper, accountKeeper *authkeeper.AccountKeeper, stakingKeeper *stakingkeeper.Keeper, - transferKeeper ibctransferkeeper.Keeper, wasmKeeper *wasmkeeper.PermissionedKeeper, wasmViewKeeper *wasmkeeper.Keeper, upgradeKeeper *upgradekeeper.Keeper) *Keeper { + wasmKeeper *wasmkeeper.PermissionedKeeper, wasmViewKeeper *wasmkeeper.Keeper, upgradeKeeper *upgradekeeper.Keeper) *Keeper { if !paramstore.HasKeyTable() { paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) @@ -140,7 +138,6 @@ func NewKeeper( bankKeeper: bankKeeper, accountKeeper: accountKeeper, stakingKeeper: stakingKeeper, - transferKeeper: transferKeeper, wasmKeeper: wasmKeeper, wasmViewKeeper: wasmViewKeeper, upgradeKeeper: upgradeKeeper, diff --git a/precompiles/common/legacy/v605/expected_keepers.go b/precompiles/common/legacy/v605/expected_keepers.go index 018b15962e..25849ba792 100644 --- a/precompiles/common/legacy/v605/expected_keepers.go +++ b/precompiles/common/legacy/v605/expected_keepers.go @@ -4,10 +4,6 @@ import ( "context" "math/big" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/04-channel/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/exported" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" @@ -16,7 +12,6 @@ import ( distrtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/distribution/types" govtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/gov/types" stakingtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/staking/types" - ibctypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" "github.com/sei-protocol/sei-chain/utils" oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" ) @@ -117,20 +112,3 @@ type DistributionKeeper interface { WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) DelegationTotalRewards(c context.Context, req *distrtypes.QueryDelegationTotalRewardsRequest) (*distrtypes.QueryDelegationTotalRewardsResponse, error) } - -type TransferKeeper interface { - Transfer(goCtx context.Context, msg *ibctypes.MsgTransfer) (*ibctypes.MsgTransferResponse, error) -} - -type ClientKeeper interface { - GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) - GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) -} - -type ConnectionKeeper interface { - GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) -} - -type ChannelKeeper interface { - GetChannel(ctx sdk.Context, portID, channelID string) (types.Channel, bool) -} diff --git a/precompiles/common/legacy/v606/expected_keepers.go b/precompiles/common/legacy/v606/expected_keepers.go index 3d79669e61..f91e986fe3 100644 --- a/precompiles/common/legacy/v606/expected_keepers.go +++ b/precompiles/common/legacy/v606/expected_keepers.go @@ -4,10 +4,6 @@ import ( "context" "math/big" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/04-channel/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/exported" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" @@ -16,7 +12,6 @@ import ( distrtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/distribution/types" govtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/gov/types" stakingtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/staking/types" - ibctypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" "github.com/sei-protocol/sei-chain/utils" oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" ) @@ -117,20 +112,3 @@ type DistributionKeeper interface { WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) DelegationTotalRewards(c context.Context, req *distrtypes.QueryDelegationTotalRewardsRequest) (*distrtypes.QueryDelegationTotalRewardsResponse, error) } - -type TransferKeeper interface { - Transfer(goCtx context.Context, msg *ibctypes.MsgTransfer) (*ibctypes.MsgTransferResponse, error) -} - -type ClientKeeper interface { - GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) - GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) -} - -type ConnectionKeeper interface { - GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) -} - -type ChannelKeeper interface { - GetChannel(ctx sdk.Context, portID, channelID string) (types.Channel, bool) -} diff --git a/precompiles/common/legacy/v610/expected_keepers.go b/precompiles/common/legacy/v610/expected_keepers.go index b975bae011..05297520b0 100644 --- a/precompiles/common/legacy/v610/expected_keepers.go +++ b/precompiles/common/legacy/v610/expected_keepers.go @@ -4,10 +4,6 @@ import ( "context" "math/big" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/04-channel/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/exported" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" @@ -16,7 +12,6 @@ import ( distrtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/distribution/types" govtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/gov/types" stakingtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/staking/types" - ibctypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" "github.com/sei-protocol/sei-chain/utils" oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" ) @@ -117,20 +112,3 @@ type DistributionKeeper interface { WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) DelegationTotalRewards(c context.Context, req *distrtypes.QueryDelegationTotalRewardsRequest) (*distrtypes.QueryDelegationTotalRewardsResponse, error) } - -type TransferKeeper interface { - Transfer(goCtx context.Context, msg *ibctypes.MsgTransfer) (*ibctypes.MsgTransferResponse, error) -} - -type ClientKeeper interface { - GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) - GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) -} - -type ConnectionKeeper interface { - GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) -} - -type ChannelKeeper interface { - GetChannel(ctx sdk.Context, portID, channelID string) (types.Channel, bool) -} diff --git a/precompiles/common/retired.go b/precompiles/common/retired.go new file mode 100644 index 0000000000..ccb1a2aada --- /dev/null +++ b/precompiles/common/retired.go @@ -0,0 +1,54 @@ +package common + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + putils "github.com/sei-protocol/sei-chain/precompiles/utils" + sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" +) + +// NewRetiredPrecompile keeps a precompile address registered while making every +// valid method call revert with reason. Unregistering an address would instead +// make calls succeed with empty output, which can appear successful to callers. +func NewRetiredPrecompile(a abi.ABI, address common.Address, name string, evmKeeper putils.EVMKeeper, reason string) *DynamicGasPrecompile { + return NewDynamicGasPrecompile(a, &retiredExecutor{ + evmKeeper: evmKeeper, + err: errors.New(reason), + revertData: encodeRevertReason(reason), + }, address, name) +} + +type retiredExecutor struct { + evmKeeper putils.EVMKeeper + err error + revertData []byte +} + +func (e *retiredExecutor) Execute(ctx sdk.Context, _ *abi.Method, _ common.Address, _ common.Address, _ []interface{}, value *big.Int, _ bool, _ *vm.EVM, _ uint64, _ *tracing.Hooks) ([]byte, uint64, error) { + if err := ValidateNonPayable(value); err != nil { + return nil, 0, err + } + return e.revertData, GetRemainingGas(ctx, e.evmKeeper), e.err +} + +func (e *retiredExecutor) EVMKeeper() putils.EVMKeeper { + return e.evmKeeper +} + +func encodeRevertReason(reason string) []byte { + stringType, err := abi.NewType("string", "", nil) + if err != nil { + panic(err) + } + reasonData, err := abi.Arguments{{Type: stringType}}.Pack(reason) + if err != nil { + panic(err) + } + return append(crypto.Keccak256([]byte("Error(string)"))[:4], reasonData...) +} diff --git a/precompiles/ibc/ibc.go b/precompiles/ibc/ibc.go index ec428d9c95..76e3adda14 100644 --- a/precompiles/ibc/ibc.go +++ b/precompiles/ibc/ibc.go @@ -2,400 +2,32 @@ package ibc import ( "embed" - "errors" - "fmt" - "math/big" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - pcommon "github.com/sei-protocol/sei-chain/precompiles/common" "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" ) -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) +const IBCAddress = "0x0000000000000000000000000000000000001009" -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) +const RetiredReason = "ibc precompile is retired; ibc transfers are disabled" -// Embed abi json file to the executable binary. Needed when importing as dependency. -// //go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} +var currentABI embed.FS +// NewPrecompile keeps the IBC address registered but retires all of its +// methods because IBC transfers are permanently disabled. func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil + return newRetiredPrecompile(pcommon.MustGetABI(currentABI, "abi.json"), keepers), nil } -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg +func newRetiredPrecompile(a abi.ABI, keepers utils.Keepers) *pcommon.DynamicGasPrecompile { + return pcommon.NewRetiredPrecompile( + a, + common.HexToAddress(IBCAddress), + "ibc", + keepers.EVMK(), + RetiredReason, + ) } diff --git a/precompiles/ibc/ibc_test.go b/precompiles/ibc/ibc_test.go index 25136a2c9e..714a42d700 100644 --- a/precompiles/ibc/ibc_test.go +++ b/precompiles/ibc/ibc_test.go @@ -1,732 +1,99 @@ package ibc_test import ( - "context" - "errors" "math/big" - "reflect" "testing" - "time" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" + "github.com/stretchr/testify/require" + "github.com/sei-protocol/sei-chain/app" + pcommon "github.com/sei-protocol/sei-chain/precompiles/common" "github.com/sei-protocol/sei-chain/precompiles/ibc" - "github.com/sei-protocol/sei-chain/precompiles/utils" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/exported" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/types" - testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper" "github.com/sei-protocol/sei-chain/x/evm/state" - "github.com/stretchr/testify/require" ) -type MockTransferKeeper struct{} - -func (tk *MockTransferKeeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) { - return nil, nil -} - -func (tk *MockTransferKeeper) SendTransfer( - ctx sdk.Context, - sourcePort, - sourceChannel string, - token sdk.Coin, - sender sdk.AccAddress, - receiver string, - timeoutHeight clienttypes.Height, - timeoutTimestamp uint64, -) error { - return nil -} - -type MockMemoTransferKeeper struct { - t require.TestingT - wantMemo string -} - -func (tk *MockMemoTransferKeeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) { - require.Equal(tk.t, tk.wantMemo, msg.Memo) - return nil, nil -} - -func (tk *MockMemoTransferKeeper) SendTransfer( - ctx sdk.Context, - sourcePort, - sourceChannel string, - token sdk.Coin, - sender sdk.AccAddress, - receiver string, - timeoutHeight clienttypes.Height, - timeoutTimestamp uint64, -) error { - return nil -} - -type MockFailedTransferTransferKeeper struct{} - -func (tk *MockFailedTransferTransferKeeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) { - return nil, errors.New("failed to send transfer") -} - -func (tk *MockFailedTransferTransferKeeper) SendTransfer( - ctx sdk.Context, - sourcePort, - sourceChannel string, - token sdk.Coin, - sender sdk.AccAddress, - receiver string, - timeoutHeight clienttypes.Height, - timeoutTimestamp uint64, -) error { - return nil -} - -func TestPrecompile_Run(t *testing.T) { - senderSeiAddress, senderEvmAddress := testkeeper.MockAddressPair() - receiverAddress := "cosmos1yykwxjzr2tv4mhx5tsf8090sdg96f2ax8fydk2" - - pre, _ := ibc.NewPrecompile(&utils.EmptyKeepers{}) - testTransfer, _ := pre.ABI.MethodById(pre.GetExecutor().(*ibc.PrecompileExecutor).TransferID) - packedTrue, _ := testTransfer.Outputs.Pack(true) - - type fields struct { - transferKeeper utils.TransferKeeper - } - - type input struct { - receiverAddr string - sourcePort string - sourceChannel string - denom string - amount *big.Int - revisionNumber uint64 - revisionHeight uint64 - timeoutTimestamp uint64 - memo string - } - type args struct { - caller common.Address - callingContract common.Address - input *input - suppliedGas uint64 - value *big.Int - isFromDelegateCall bool - } - - commonArgs := args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "transfer", - sourceChannel: "channel-0", - denom: "denom", - amount: big.NewInt(100), - revisionNumber: 1, - revisionHeight: 1, - timeoutTimestamp: 1, - }, - suppliedGas: uint64(1000000), - value: nil, - } - - tests := []struct { - name string - fields fields - args args - wantBz []byte - wantRemainingGas uint64 - wantErr bool - wantErrMsg string - }{ - { - name: "successful transfer: with amount > 0 between EVM addresses", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: commonArgs, - wantBz: packedTrue, - wantRemainingGas: 995840, - wantErr: false, - }, - { - name: "failed transfer: internal error", - fields: fields{transferKeeper: &MockFailedTransferTransferKeeper{}}, - args: commonArgs, - wantBz: nil, - wantErr: true, - wantErrMsg: "failed to send transfer", - }, - { - name: "failed transfer: caller not whitelisted", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{caller: senderEvmAddress, callingContract: common.Address{}, input: commonArgs.input, suppliedGas: 1000000, value: nil, isFromDelegateCall: true}, - wantBz: nil, - wantErr: true, - wantErrMsg: "cannot delegatecall IBC", - }, - { - name: "failed transfer: value is not nil", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{caller: senderEvmAddress, callingContract: common.Address{}, input: commonArgs.input, suppliedGas: 1000000, value: big.NewInt(100), isFromDelegateCall: true}, - wantBz: nil, - wantErr: true, - wantErrMsg: "sending funds to a non-payable function", - }, - { - name: "failed transfer: empty sourcePort", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "", // empty sourcePort - sourceChannel: "channel-0", - denom: "denom", - amount: big.NewInt(100), - revisionNumber: 1, - revisionHeight: 1, - timeoutTimestamp: 1, - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: nil, - wantErr: true, - wantErrMsg: "port cannot be empty", - }, - { - name: "failed transfer: empty sourceChannel", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "transfer", - sourceChannel: "", - denom: "denom", - amount: big.NewInt(100), - revisionNumber: 1, - revisionHeight: 1, - timeoutTimestamp: 1, - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: nil, - wantErr: true, - wantErrMsg: "channelID cannot be empty", - }, - { - name: "failed transfer: invalid denom", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "transfer", - sourceChannel: "channel-0", - denom: "", - amount: big.NewInt(100), - revisionNumber: 1, - revisionHeight: 1, - timeoutTimestamp: 1, - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: nil, - wantErr: true, - wantErrMsg: "invalid denom", - }, - { - name: "failed transfer: invalid receiver address", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: "", - sourcePort: "transfer", - sourceChannel: "channel-0", - denom: "", - amount: big.NewInt(100), - revisionNumber: 1, - revisionHeight: 1, - timeoutTimestamp: 1, - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: nil, - wantErr: true, - wantErrMsg: "receiverAddress is not a string or empty", - }, - { - name: "memo is added to the transfer if passed", - fields: fields{transferKeeper: &MockMemoTransferKeeper{t: t, wantMemo: "test memo"}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "transfer", - sourceChannel: "channel-0", - denom: "usei", - amount: big.NewInt(100), - memo: "test memo", - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: packedTrue, - wantRemainingGas: 995720, - wantErr: false, - }, - { - name: "memo is not added to the transfer if not passed", - fields: fields{transferKeeper: &MockMemoTransferKeeper{t: t, wantMemo: ""}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "transfer", - sourceChannel: "channel-0", - denom: "usei", - amount: big.NewInt(100), - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: packedTrue, - wantRemainingGas: 995843, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testApp := testkeeper.EVMTestApp - ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) - k := &testApp.EvmKeeper - k.SetAddressMapping(ctx, senderSeiAddress, senderEvmAddress) - stateDb := state.NewDBImpl(ctx, k, true) - evm := vm.EVM{ - StateDB: stateDb, - TxContext: vm.TxContext{Origin: senderEvmAddress}, - } - p, _ := ibc.NewPrecompile(&app.PrecompileKeepers{ - TransferKeeper: tt.fields.transferKeeper, - EVMKeeper: k, - }) - transfer, err := p.ABI.MethodById(p.GetExecutor().(*ibc.PrecompileExecutor).TransferID) - require.Nil(t, err) - inputs, err := transfer.Inputs.Pack(tt.args.input.receiverAddr, - tt.args.input.sourcePort, tt.args.input.sourceChannel, tt.args.input.denom, tt.args.input.amount, - tt.args.input.revisionNumber, tt.args.input.revisionHeight, tt.args.input.timeoutTimestamp, - tt.args.input.memo) - require.Nil(t, err) - gotBz, gotRemainingGas, err := p.RunAndCalculateGas(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*ibc.PrecompileExecutor).TransferID, inputs...), tt.args.suppliedGas, tt.args.value, nil, false, tt.args.isFromDelegateCall) - if (err != nil) != tt.wantErr { - t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr) - return - } - if err != nil { - require.Equal(t, vm.ErrExecutionReverted, err) - require.Nil(t, gotBz) - } else if !reflect.DeepEqual(gotBz, tt.wantBz) { - t.Errorf("Run() gotRet = %v, want %v", gotBz, tt.wantBz) - } - - if !reflect.DeepEqual(gotRemainingGas, tt.wantRemainingGas) { - t.Errorf("Run() gotRemainingGas = %v, want %v", gotRemainingGas, tt.wantRemainingGas) - } - }) - } -} - -func TestTransferWithDefaultTimeoutPrecompile_Run(t *testing.T) { - senderSeiAddress, senderEvmAddress := testkeeper.MockAddressPair() - receiverAddress := "cosmos1yykwxjzr2tv4mhx5tsf8090sdg96f2ax8fydk2" - - type fields struct { - transferKeeper utils.TransferKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - } - - type input struct { - receiverAddr string - sourcePort string - sourceChannel string - denom string - amount *big.Int - memo string - } - type args struct { - caller common.Address - callingContract common.Address - input *input - suppliedGas uint64 - value *big.Int - isFromDelegateCall bool - } - - commonArgs := args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "transfer", - sourceChannel: "channel-0", - denom: "denom", - amount: big.NewInt(100), - }, - suppliedGas: uint64(1000000), - value: nil, - } - - tests := []struct { - name string - fields fields - args args - wantBz []byte - wantRemainingGas uint64 - wantErr bool - wantErrMsg string - }{ - { - name: "failed transfer: caller not whitelisted", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{caller: senderEvmAddress, callingContract: common.Address{}, input: commonArgs.input, suppliedGas: 1000000, value: nil, isFromDelegateCall: true}, - wantBz: nil, - wantErr: true, - wantErrMsg: "cannot delegatecall IBC", - }, - { - name: "failed transfer: value is not nil", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{caller: senderEvmAddress, callingContract: common.Address{}, input: commonArgs.input, suppliedGas: 1000000, value: big.NewInt(100), isFromDelegateCall: true}, - wantBz: nil, - wantErr: true, - wantErrMsg: "sending funds to a non-payable function", - }, - { - name: "failed transfer: empty sourcePort", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "", // empty sourcePort - sourceChannel: "channel-0", - denom: "denom", - amount: big.NewInt(100), - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: nil, - wantErr: true, - wantErrMsg: "port cannot be empty", - }, - { - name: "failed transfer: empty sourceChannel", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "transfer", - sourceChannel: "", - denom: "denom", - amount: big.NewInt(100), - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: nil, - wantErr: true, - wantErrMsg: "channelID cannot be empty", - }, - { - name: "failed transfer: invalid denom", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: receiverAddress, - sourcePort: "transfer", - sourceChannel: "channel-0", - denom: "", - amount: big.NewInt(100), - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: nil, - wantErr: true, - wantErrMsg: "invalid denom", - }, - { - name: "failed transfer: invalid receiver address", - fields: fields{transferKeeper: &MockTransferKeeper{}}, - args: args{ - caller: senderEvmAddress, - callingContract: senderEvmAddress, - input: &input{ - receiverAddr: "", - sourcePort: "transfer", - sourceChannel: "channel-0", - denom: "", - amount: big.NewInt(100), - }, - suppliedGas: uint64(1000000), - value: nil, - }, - wantBz: nil, - wantErr: true, - wantErrMsg: "receiverAddress is not a string or empty", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testApp := testkeeper.EVMTestApp - ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) - k := &testApp.EvmKeeper - k.SetAddressMapping(ctx, senderSeiAddress, senderEvmAddress) - stateDb := state.NewDBImpl(ctx, k, true) - evm := vm.EVM{ - StateDB: stateDb, - TxContext: vm.TxContext{Origin: senderEvmAddress}, - } - - p, _ := ibc.NewPrecompile(&app.PrecompileKeepers{ - TransferKeeper: tt.fields.transferKeeper, - EVMKeeper: k, - ClientKeeper: tt.fields.clientKeeper, - ChannelKeeper: tt.fields.channelKeeper, - ConnectionKeeper: tt.fields.connectionKeeper, - }) - transfer, err := p.ABI.MethodById(p.GetExecutor().(*ibc.PrecompileExecutor).TransferWithDefaultTimeoutID) - require.Nil(t, err) - inputs, err := transfer.Inputs.Pack(tt.args.input.receiverAddr, - tt.args.input.sourcePort, tt.args.input.sourceChannel, tt.args.input.denom, tt.args.input.amount, - tt.args.input.memo) - require.Nil(t, err) - gotBz, gotRemainingGas, err := p.RunAndCalculateGas(&evm, - tt.args.caller, - tt.args.callingContract, - append(p.GetExecutor().(*ibc.PrecompileExecutor).TransferWithDefaultTimeoutID, inputs...), - tt.args.suppliedGas, - tt.args.value, +func TestEveryIBCVersionIsRetired(t *testing.T) { + testApp := app.Setup(t, false, true, false) + ctx := testApp.NewContext(false, tmtypes.Header{}) + evm := &vm.EVM{StateDB: state.NewDBImpl(ctx, &testApp.EvmKeeper, true)} + + versioned := ibc.GetVersioned("v6.6", testApp.GetPrecompileKeepers()) + require.Len(t, versioned, 15) + + for version, contract := range versioned { + t.Run(version, func(t *testing.T) { + precompile, ok := contract.(*pcommon.DynamicGasPrecompile) + require.True(t, ok) + + input := validCallData(t, precompile.GetABI()) + ret, _, err := precompile.RunAndCalculateGas( + evm, + common.Address{}, + common.Address{}, + input, + 1_000_000, + nil, nil, false, - tt.args.isFromDelegateCall, + false, ) - if (err != nil) != tt.wantErr { - t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr) - return - } - if err != nil { - require.Equal(t, vm.ErrExecutionReverted, err) - require.Nil(t, gotBz) - } else if !reflect.DeepEqual(gotBz, tt.wantBz) { - t.Errorf("Run() gotRet = %v, want %v", gotBz, tt.wantBz) - } - if !reflect.DeepEqual(gotRemainingGas, tt.wantRemainingGas) { - t.Errorf("Run() gotRemainingGas = %v, want %v", gotRemainingGas, tt.wantRemainingGas) - } - }) - } -} + require.ErrorIs(t, err, vm.ErrExecutionReverted) -func TestPrecompile_GetAdjustedHeight(t *testing.T) { - type args struct { - latestConsensusHeight clienttypes.Height - } - tests := []struct { - name string - args args - want clienttypes.Height - wantErr bool - }{ - { - name: "height is adjusted with defaults", - args: args{ - latestConsensusHeight: clienttypes.NewHeight(2, 3), - }, - want: clienttypes.Height{ - RevisionNumber: 2, - RevisionHeight: 1003, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := ibc.GetAdjustedHeight(tt.args.latestConsensusHeight) - if (err != nil) != tt.wantErr { - t.Errorf("GetAdjustedHeight() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GetAdjustedHeight() got = %v, want %v", got, tt.want) - } + reason, err := abi.UnpackRevert(ret) + require.NoError(t, err) + require.Equal(t, ibc.RetiredReason, reason) }) } } -type MockClientKeeper struct { - consensusState *MockConsensusState - returnConsensusState bool -} - -func (ck *MockClientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { - return nil, false -} - -func (ck *MockClientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { - return ck.consensusState, ck.returnConsensusState -} - -type MockConsensusState struct { - timestamp uint64 -} - -func (m *MockConsensusState) Reset() { - panic("implement me") -} - -func (m *MockConsensusState) String() string { - panic("implement me") -} - -func (m *MockConsensusState) ProtoMessage() { - panic("implement me") -} - -func (m *MockConsensusState) ClientType() string { - return "mock" -} - -func (m *MockConsensusState) GetRoot() exported.Root { - return nil -} - -func (m *MockConsensusState) GetTimestamp() uint64 { - return m.timestamp -} - -func (m *MockConsensusState) ValidateBasic() error { - return nil -} - -func TestPrecompile_GetAdjustedTimestamp(t *testing.T) { - type fields struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - } - type args struct { - ctx sdk.Context - clientId string - height clienttypes.Height - } - timestampSeconds := 1714680155 - ctx := sdk.Context{} - tests := []struct { - name string - fields fields - args args - want uint64 - wantErr bool - }{ - { - name: "if consensus timestamp is less than the given time, return the given time adjusted with default", - fields: fields{ - clientKeeper: &MockClientKeeper{ - consensusState: &MockConsensusState{ - timestamp: uint64(timestampSeconds - 1), - }, - returnConsensusState: true, - }, - }, - args: args{ - ctx: ctx.WithBlockTime(time.Unix(int64(timestampSeconds), 0)), - }, - want: uint64(timestampSeconds)*1_000_000_000 + uint64((time.Duration(10) * time.Minute).Nanoseconds()), - wantErr: false, - }, - { - name: "if consensus state is not found, return the given time adjusted with default", - fields: fields{ - clientKeeper: &MockClientKeeper{ - returnConsensusState: false, - }, - }, - args: args{ - ctx: ctx.WithBlockTime(time.Unix(int64(timestampSeconds), 0)), - }, - want: uint64(timestampSeconds)*1_000_000_000 + uint64((time.Duration(10) * time.Minute).Nanoseconds()), - wantErr: false, - }, - { - name: "if time from local clock can not be retrieved, return error", - fields: fields{ - clientKeeper: &MockClientKeeper{ - returnConsensusState: false, - }, - }, - args: args{ - ctx: ctx.WithBlockTime(time.Unix(int64(0), 0)), - }, - wantErr: true, - }, - { - name: "if consensus timestamp is > than the given time, return the consensus time adjusted with default", - fields: fields{ - clientKeeper: &MockClientKeeper{ - consensusState: &MockConsensusState{ - timestamp: uint64(timestampSeconds+1) * 1_000_000_000, - }, - returnConsensusState: true, - }, - }, - args: args{ - ctx: ctx.WithBlockTime(time.Unix(int64(timestampSeconds), 0)), - }, - want: uint64(timestampSeconds+1)*1_000_000_000 + uint64((time.Duration(10) * time.Minute).Nanoseconds()), - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p, _ := ibc.NewPrecompile(&app.PrecompileKeepers{ - TransferKeeper: tt.fields.transferKeeper, - EVMKeeper: tt.fields.evmKeeper, - ClientKeeper: tt.fields.clientKeeper, - ChannelKeeper: tt.fields.channelKeeper, - ConnectionKeeper: tt.fields.connectionKeeper, - }) - got, err := p.GetExecutor().(*ibc.PrecompileExecutor).GetAdjustedTimestamp(tt.args.ctx, tt.args.clientId, tt.args.height) - if (err != nil) != tt.wantErr { - t.Errorf("GetAdjustedTimestamp() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("GetAdjustedTimestamp() got = %v, want %v", got, tt.want) +func TestRetiredIBCPrecompileRemainsNonPayable(t *testing.T) { + testApp := app.Setup(t, false, true, false) + ctx := testApp.NewContext(false, tmtypes.Header{}) + evm := &vm.EVM{StateDB: state.NewDBImpl(ctx, &testApp.EvmKeeper, true)} + + precompile, err := ibc.NewPrecompile(testApp.GetPrecompileKeepers()) + require.NoError(t, err) + + ret, _, err := precompile.RunAndCalculateGas( + evm, + common.Address{}, + common.Address{}, + validCallData(t, precompile.GetABI()), + 1_000_000, + big.NewInt(1), + nil, + false, + false, + ) + require.ErrorIs(t, err, vm.ErrExecutionReverted) + require.Empty(t, ret) +} + +func validCallData(t *testing.T, contractABI abi.ABI) []byte { + t.Helper() + + method := contractABI.Methods["transferWithDefaultTimeout"] + args := make([]interface{}, len(method.Inputs)) + for i, input := range method.Inputs { + switch input.Type.T { + case abi.StringTy: + args[i] = "" + case abi.UintTy: + if input.Type.Size == 256 { + args[i] = new(big.Int) + } else { + args[i] = uint64(0) } - }) + default: + t.Fatalf("unsupported ABI input type %s", input.Type.String()) + } } + + encoded, err := method.Inputs.Pack(args...) + require.NoError(t, err) + return append(method.ID, encoded...) } diff --git a/precompiles/ibc/legacy/v552/ibc.go b/precompiles/ibc/legacy/v552/ibc.go deleted file mode 100644 index b7bb3539d9..0000000000 --- a/precompiles/ibc/legacy/v552/ibc.go +++ /dev/null @@ -1,450 +0,0 @@ -package v552 - -import ( - "bytes" - "embed" - "errors" - "fmt" - "math/big" - - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - - "github.com/sei-protocol/sei-chain/sei-cosmos/types/bech32" - - putils "github.com/sei-protocol/sei-chain/precompiles/utils" - "github.com/sei-protocol/sei-chain/utils" - "github.com/sei-protocol/sei-chain/x/evm/state" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v552" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -var _ vm.PrecompiledContract = &Precompile{} -var _ vm.DynamicGasPrecompiledContract = &Precompile{} - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -func GetABI() abi.ABI { - abiBz, err := f.ReadFile("abi.json") - if err != nil { - panic(err) - } - - newAbi, err := abi.JSON(bytes.NewReader(abiBz)) - if err != nil { - panic(err) - } - return newAbi -} - -type Precompile struct { - pcommon.Precompile - address common.Address - transferKeeper putils.TransferKeeper - evmKeeper putils.EVMKeeper - clientKeeper putils.ClientKeeper - connectionKeeper putils.ConnectionKeeper - channelKeeper putils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers putils.Keepers) (*Precompile, error) { - newAbi := GetABI() - - p := &Precompile{ - Precompile: pcommon.Precompile{ABI: newAbi}, - address: common.HexToAddress(IBCAddress), - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return p, nil -} - -// RequiredGas returns the required bare minimum gas to execute the precompile. -func (p Precompile) RequiredGas(input []byte) uint64 { - methodID, err := pcommon.ExtractMethodID(input) - if err != nil { - return pcommon.UnknownMethodCallGas - } - - method, err := p.MethodById(methodID) - if err != nil { - // This should never happen since this method is going to fail during Run - return pcommon.UnknownMethodCallGas - } - - return p.Precompile.RequiredGas(input, p.IsTransaction(method.Name)) -} - -func (p Precompile) RunAndCalculateGas(evm *vm.EVM, caller common.Address, callingContract common.Address, input []byte, suppliedGas uint64, value *big.Int, _ *tracing.Hooks, readOnly bool, _ bool) (ret []byte, remainingGas uint64, err error) { - defer func() { - if err != nil { - state.GetDBImpl(evm.StateDB).SetPrecompileError(err) - } - }() - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - ctx, method, args, err := p.Prepare(evm, input) - if err != nil { - return nil, 0, err - } - if caller.Cmp(callingContract) != 0 { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - gasMultiplier := p.evmKeeper.GetPriorityNormalizer(ctx) - gasLimitBigInt := new(big.Int).Mul(new(big.Int).SetUint64(suppliedGas), gasMultiplier.TruncateInt().BigInt()) - if gasLimitBigInt.Cmp(utils.BigMaxU64) > 0 { - gasLimitBigInt = utils.BigMaxU64 - } - ctx = ctx.WithGasMeter(sdk.NewGasMeterWithMultiplier(ctx, gasLimitBigInt.Uint64())) - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p Precompile) Run(*vm.EVM, common.Address, common.Address, []byte, *big.Int, bool, bool, *tracing.Hooks) (bz []byte, err error) { - panic("static gas Run is not implemented for dynamic gas precompile") -} - -func (p Precompile) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 8); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - err = p.transferKeeper.SendTransfer( - ctx, - validatedArgs.port, - validatedArgs.channelID, - coin, - validatedArgs.senderSeiAddr, - validatedArgs.receiverAddressString, - height, - timeoutTimestamp) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p Precompile) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 5); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - err = p.transferKeeper.SendTransfer( - ctx, - validatedArgs.port, - validatedArgs.channelID, - coin, - validatedArgs.senderSeiAddr, - validatedArgs.receiverAddressString, - height, - timeoutTimestamp) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (Precompile) IsTransaction(method string) bool { - switch method { - case TransferMethod: - return true - default: - return false - } -} - -func (p Precompile) Address() common.Address { - return p.address -} - -func (p Precompile) GetName() string { - return "ibc" -} - -func (p Precompile) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, fmt.Errorf("EVM address %s is not associated", addr.Hex()) - } - return seiAddr, nil -} - -func (p Precompile) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p Precompile) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p Precompile) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p Precompile) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok { - return nil, errors.New("receiverAddress is not a string") - } - _, bz, err := bech32.DecodeAndConvert(receiverAddressString) - if err != nil { - return nil, err - } - err = sdk.VerifyAddressFormat(bz) - if err != nil { - return nil, err - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} diff --git a/precompiles/ibc/legacy/v555/ibc.go b/precompiles/ibc/legacy/v555/ibc.go deleted file mode 100644 index 696a3ff6dc..0000000000 --- a/precompiles/ibc/legacy/v555/ibc.go +++ /dev/null @@ -1,481 +0,0 @@ -package v555 - -import ( - "bytes" - "embed" - "errors" - "fmt" - "math/big" - - putils "github.com/sei-protocol/sei-chain/precompiles/utils" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - - "github.com/sei-protocol/sei-chain/sei-cosmos/types/bech32" - - "github.com/sei-protocol/sei-chain/utils" - "github.com/sei-protocol/sei-chain/x/evm/state" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v555" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -var _ vm.PrecompiledContract = &Precompile{} -var _ vm.DynamicGasPrecompiledContract = &Precompile{} - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -func GetABI() abi.ABI { - abiBz, err := f.ReadFile("abi.json") - if err != nil { - panic(err) - } - - newAbi, err := abi.JSON(bytes.NewReader(abiBz)) - if err != nil { - panic(err) - } - return newAbi -} - -type Precompile struct { - pcommon.Precompile - address common.Address - transferKeeper putils.TransferKeeper - evmKeeper putils.EVMKeeper - clientKeeper putils.ClientKeeper - connectionKeeper putils.ConnectionKeeper - channelKeeper putils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers putils.Keepers) (*Precompile, error) { - newAbi := GetABI() - - p := &Precompile{ - Precompile: pcommon.Precompile{ABI: newAbi}, - address: common.HexToAddress(IBCAddress), - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return p, nil -} - -// RequiredGas returns the required bare minimum gas to execute the precompile. -func (p Precompile) RequiredGas(input []byte) uint64 { - methodID, err := pcommon.ExtractMethodID(input) - if err != nil { - return pcommon.UnknownMethodCallGas - } - - method, err := p.MethodById(methodID) - if err != nil { - // This should never happen since this method is going to fail during Run - return pcommon.UnknownMethodCallGas - } - - return p.Precompile.RequiredGas(input, p.IsTransaction(method.Name)) -} - -func (p Precompile) RunAndCalculateGas(evm *vm.EVM, caller common.Address, callingContract common.Address, input []byte, suppliedGas uint64, _ *big.Int, _ *tracing.Hooks, readOnly bool, _ bool) (ret []byte, remainingGas uint64, err error) { - defer func() { - if err != nil { - state.GetDBImpl(evm.StateDB).SetPrecompileError(err) - } - }() - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - ctx, method, args, err := p.Prepare(evm, input) - if err != nil { - return nil, 0, err - } - if caller.Cmp(callingContract) != 0 { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - gasMultiplier := p.evmKeeper.GetPriorityNormalizer(ctx) - gasLimitBigInt := new(big.Int).Mul(new(big.Int).SetUint64(suppliedGas), gasMultiplier.TruncateInt().BigInt()) - if gasLimitBigInt.Cmp(utils.BigMaxU64) > 0 { - gasLimitBigInt = utils.BigMaxU64 - } - ctx = ctx.WithGasMeter(sdk.NewGasMeterWithMultiplier(ctx, gasLimitBigInt.Uint64())) - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p Precompile) Run(*vm.EVM, common.Address, common.Address, []byte, *big.Int, bool, bool, *tracing.Hooks) (bz []byte, err error) { - panic("static gas Run is not implemented for dynamic gas precompile") -} - -func (p Precompile) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p Precompile) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (Precompile) IsTransaction(method string) bool { - switch method { - case TransferMethod: - return true - case TransferWithDefaultTimeoutMethod: - return true - default: - return false - } -} - -func (p Precompile) Address() common.Address { - return p.address -} - -func (p Precompile) GetName() string { - return "ibc" -} - -func (p Precompile) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, fmt.Errorf("EVM address %s is not associated", addr.Hex()) - } - return seiAddr, nil -} - -func (p Precompile) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p Precompile) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p Precompile) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p Precompile) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok { - return nil, errors.New("receiverAddress is not a string") - } - _, bz, err := bech32.DecodeAndConvert(receiverAddressString) - if err != nil { - return nil, err - } - err = sdk.VerifyAddressFormat(bz) - if err != nil { - return nil, err - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v562/ibc.go b/precompiles/ibc/legacy/v562/ibc.go deleted file mode 100644 index 698059cb7b..0000000000 --- a/precompiles/ibc/legacy/v562/ibc.go +++ /dev/null @@ -1,420 +0,0 @@ -package v562 - -import ( - "bytes" - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - putils "github.com/sei-protocol/sei-chain/precompiles/utils" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-cosmos/types/bech32" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v562" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -func GetABI() abi.ABI { - abiBz, err := f.ReadFile("abi.json") - if err != nil { - panic(err) - } - - newAbi, err := abi.JSON(bytes.NewReader(abiBz)) - if err != nil { - panic(err) - } - return newAbi -} - -type PrecompileExecutor struct { - transferKeeper putils.TransferKeeper - evmKeeper putils.EVMKeeper - clientKeeper putils.ClientKeeper - connectionKeeper putils.ConnectionKeeper - channelKeeper putils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers putils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := GetABI() - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, hooks *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if caller.Cmp(callingContract) != 0 { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() putils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok { - return nil, errors.New("receiverAddress is not a string") - } - _, bz, err := bech32.DecodeAndConvert(receiverAddressString) - if err != nil { - return nil, err - } - err = sdk.VerifyAddressFormat(bz) - if err != nil { - return nil, err - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v580/ibc.go b/precompiles/ibc/legacy/v580/ibc.go deleted file mode 100644 index 2c57648404..0000000000 --- a/precompiles/ibc/legacy/v580/ibc.go +++ /dev/null @@ -1,406 +0,0 @@ -package v580 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-cosmos/types/bech32" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v580" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, hooks *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if caller.Cmp(callingContract) != 0 { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok { - return nil, errors.New("receiverAddress is not a string") - } - _, bz, err := bech32.DecodeAndConvert(receiverAddressString) - if err != nil { - return nil, err - } - err = sdk.VerifyAddressFormat(bz) - if err != nil { - return nil, err - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v601/ibc.go b/precompiles/ibc/legacy/v601/ibc.go deleted file mode 100644 index 2409bed68b..0000000000 --- a/precompiles/ibc/legacy/v601/ibc.go +++ /dev/null @@ -1,406 +0,0 @@ -package v601 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-cosmos/types/bech32" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v66" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok { - return nil, errors.New("receiverAddress is not a string") - } - _, bz, err := bech32.DecodeAndConvert(receiverAddressString) - if err != nil { - return nil, err - } - err = sdk.VerifyAddressFormat(bz) - if err != nil { - return nil, err - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v603/ibc.go b/precompiles/ibc/legacy/v603/ibc.go deleted file mode 100644 index 4bf2d17e52..0000000000 --- a/precompiles/ibc/legacy/v603/ibc.go +++ /dev/null @@ -1,397 +0,0 @@ -package v603 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v66" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v605/ibc.go b/precompiles/ibc/legacy/v605/ibc.go deleted file mode 100644 index 8fdfe973d0..0000000000 --- a/precompiles/ibc/legacy/v605/ibc.go +++ /dev/null @@ -1,401 +0,0 @@ -package v605 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v605" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v606/ibc.go b/precompiles/ibc/legacy/v606/ibc.go deleted file mode 100644 index b76057374b..0000000000 --- a/precompiles/ibc/legacy/v606/ibc.go +++ /dev/null @@ -1,401 +0,0 @@ -package v606 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v606" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v610/ibc.go b/precompiles/ibc/legacy/v610/ibc.go deleted file mode 100644 index d0e571f8e7..0000000000 --- a/precompiles/ibc/legacy/v610/ibc.go +++ /dev/null @@ -1,401 +0,0 @@ -package v610 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v606" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v614/ibc.go b/precompiles/ibc/legacy/v614/ibc.go deleted file mode 100644 index 97e76b3719..0000000000 --- a/precompiles/ibc/legacy/v614/ibc.go +++ /dev/null @@ -1,401 +0,0 @@ -package v614 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v614" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v620/ibc.go b/precompiles/ibc/legacy/v620/ibc.go deleted file mode 100644 index 830b15c57e..0000000000 --- a/precompiles/ibc/legacy/v620/ibc.go +++ /dev/null @@ -1,401 +0,0 @@ -package v620 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v620" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v630/ibc.go b/precompiles/ibc/legacy/v630/ibc.go deleted file mode 100644 index 33cee9208a..0000000000 --- a/precompiles/ibc/legacy/v630/ibc.go +++ /dev/null @@ -1,401 +0,0 @@ -package v630 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v630" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v640/ibc.go b/precompiles/ibc/legacy/v640/ibc.go deleted file mode 100644 index 77597bd1e1..0000000000 --- a/precompiles/ibc/legacy/v640/ibc.go +++ /dev/null @@ -1,401 +0,0 @@ -package v640 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v640" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v65/ibc.go b/precompiles/ibc/legacy/v65/ibc.go deleted file mode 100644 index a2db1705b5..0000000000 --- a/precompiles/ibc/legacy/v65/ibc.go +++ /dev/null @@ -1,401 +0,0 @@ -package v65 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v65" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/legacy/v66/ibc.go b/precompiles/ibc/legacy/v66/ibc.go deleted file mode 100644 index 2d705225ce..0000000000 --- a/precompiles/ibc/legacy/v66/ibc.go +++ /dev/null @@ -1,403 +0,0 @@ -// Code generated by scripts/bump_version; DO NOT EDIT. - -package v66 - -import ( - "embed" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - - pcommon "github.com/sei-protocol/sei-chain/precompiles/common/legacy/v66" - "github.com/sei-protocol/sei-chain/precompiles/utils" - evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" -) - -const ( - TransferMethod = "transfer" - TransferWithDefaultTimeoutMethod = "transferWithDefaultTimeout" -) - -const ( - IBCAddress = "0x0000000000000000000000000000000000001009" -) - -// Embed abi json file to the executable binary. Needed when importing as dependency. -// -//go:embed abi.json -var f embed.FS - -type PrecompileExecutor struct { - transferKeeper utils.TransferKeeper - evmKeeper utils.EVMKeeper - clientKeeper utils.ClientKeeper - connectionKeeper utils.ConnectionKeeper - channelKeeper utils.ChannelKeeper - - TransferID []byte - TransferWithDefaultTimeoutID []byte -} - -func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { - newAbi := pcommon.MustGetABI(f, "abi.json") - - p := &PrecompileExecutor{ - transferKeeper: keepers.TransferK(), - evmKeeper: keepers.EVMK(), - clientKeeper: keepers.ClientK(), - connectionKeeper: keepers.ConnectionK(), - channelKeeper: keepers.ChannelK(), - } - - for name, m := range newAbi.Methods { - switch name { - case TransferMethod: - p.TransferID = m.ID - case TransferWithDefaultTimeoutMethod: - p.TransferWithDefaultTimeoutID = m.ID - } - } - - return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(IBCAddress), "ibc"), nil -} - -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, _ *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { - if err = pcommon.ValidateNonPayable(value); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, 0, errors.New("cannot call IBC precompile from staticcall") - } - if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, 0, errors.New("cannot delegatecall IBC") - } - - switch method.Name { - case TransferMethod: - return p.transfer(ctx, method, args, caller) - case TransferWithDefaultTimeoutMethod: - return p.transferWithDefaultTimeout(ctx, method, args, caller) - } - return -} - -func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { - return p.evmKeeper -} - -func (p PrecompileExecutor) transfer(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 9); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - revisionNumber, ok := args[5].(uint64) - if !ok { - rerr = errors.New("revisionNumber is not a uint64") - return - } - - revisionHeight, ok := args[6].(uint64) - if !ok { - rerr = errors.New("revisionHeight is not a uint64") - return - } - - height := clienttypes.Height{ - RevisionNumber: revisionNumber, - RevisionHeight: revisionHeight, - } - - timeoutTimestamp, ok := args[7].(uint64) - if !ok { - rerr = errors.New("timeoutTimestamp is not a uint64") - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[8], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) transferWithDefaultTimeout(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { - defer func() { - if err := recover(); err != nil { - ret = nil - remainingGas = 0 - rerr = fmt.Errorf("%s", err) - return - } - }() - - if err := pcommon.ValidateArgsLength(args, 6); err != nil { - rerr = err - return - } - validatedArgs, err := p.validateCommonArgs(ctx, args, caller) - if err != nil { - rerr = err - return - } - - if validatedArgs.amount.Cmp(big.NewInt(0)) == 0 { - // short circuit - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return - } - - coin := sdk.Coin{ - Denom: validatedArgs.denom, - Amount: sdk.NewIntFromBigInt(validatedArgs.amount), - } - - connection, err := p.getChannelConnection(ctx, validatedArgs.port, validatedArgs.channelID) - - if err != nil { - rerr = err - return - } - - latestConsensusHeight, err := p.getConsensusLatestHeight(ctx, *connection) - if err != nil { - rerr = err - return - } - - height, err := GetAdjustedHeight(*latestConsensusHeight) - if err != nil { - rerr = err - return - } - - timeoutTimestamp, err := p.GetAdjustedTimestamp(ctx, connection.ClientId, *latestConsensusHeight) - if err != nil { - rerr = err - return - } - - msg := types.MsgTransfer{ - SourcePort: validatedArgs.port, - SourceChannel: validatedArgs.channelID, - Token: coin, - Sender: validatedArgs.senderSeiAddr.String(), - Receiver: validatedArgs.receiverAddressString, - TimeoutHeight: height, - TimeoutTimestamp: timeoutTimestamp, - } - - msg = addMemo(args[5], msg) - - err = msg.ValidateBasic() - if err != nil { - rerr = err - return - } - - _, err = p.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), &msg) - - if err != nil { - rerr = err - return - } - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - ret, rerr = method.Outputs.Pack(true) - return -} - -func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) { - addr := arg.(common.Address) - if addr == (common.Address{}) { - return nil, errors.New("invalid addr") - } - seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, addr) - if !found { - return nil, evmtypes.NewAssociationMissingErr(addr.Hex()) - } - return seiAddr, nil -} - -func (p PrecompileExecutor) getChannelConnection(ctx sdk.Context, port string, channelID string) (*connectiontypes.ConnectionEnd, error) { - channel, found := p.channelKeeper.GetChannel(ctx, port, channelID) - if !found { - return nil, errors.New("channel not found") - } - - connection, found := p.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) - - if !found { - return nil, errors.New("connection not found") - } - return &connection, nil -} - -func (p PrecompileExecutor) getConsensusLatestHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd) (*clienttypes.Height, error) { - clientState, found := p.clientKeeper.GetClientState(ctx, connection.ClientId) - - if !found { - return nil, errors.New("could not get the client state") - } - - latestHeight := clientState.GetLatestHeight() - return &clienttypes.Height{ - RevisionNumber: latestHeight.GetRevisionNumber(), - RevisionHeight: latestHeight.GetRevisionHeight(), - }, nil -} - -func GetAdjustedHeight(latestConsensusHeight clienttypes.Height) (clienttypes.Height, error) { - defaultTimeoutHeight, err := clienttypes.ParseHeight(types.DefaultRelativePacketTimeoutHeight) - if err != nil { - return clienttypes.Height{}, err - } - - absoluteHeight := latestConsensusHeight - absoluteHeight.RevisionNumber += defaultTimeoutHeight.RevisionNumber - absoluteHeight.RevisionHeight += defaultTimeoutHeight.RevisionHeight - return absoluteHeight, nil -} - -func (p PrecompileExecutor) GetAdjustedTimestamp(ctx sdk.Context, clientId string, height clienttypes.Height) (uint64, error) { - consensusState, found := p.clientKeeper.GetClientConsensusState(ctx, clientId, height) - var consensusStateTimestamp uint64 - if found { - consensusStateTimestamp = consensusState.GetTimestamp() - } - - defaultRelativePacketTimeoutTimestamp := types.DefaultRelativePacketTimeoutTimestamp - blockTime := ctx.BlockTime().UnixNano() - if blockTime > 0 { - now := uint64(blockTime) - if now > consensusStateTimestamp { - return now + defaultRelativePacketTimeoutTimestamp, nil - } else { - return consensusStateTimestamp + defaultRelativePacketTimeoutTimestamp, nil - } - } else { - return 0, errors.New("block time is not greater than Jan 1st, 1970 12:00 AM") - } -} - -type ValidatedArgs struct { - senderSeiAddr sdk.AccAddress - receiverAddressString string - port string - channelID string - denom string - amount *big.Int -} - -func (p PrecompileExecutor) validateCommonArgs(ctx sdk.Context, args []interface{}, caller common.Address) (*ValidatedArgs, error) { - senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller) - if !ok { - return nil, errors.New("caller is not a valid SEI address") - } - - receiverAddressString, ok := args[0].(string) - if !ok || receiverAddressString == "" { - return nil, errors.New("receiverAddress is not a string or empty") - } - - port, ok := args[1].(string) - if !ok { - return nil, errors.New("port is not a string") - } - if port == "" { - return nil, errors.New("port cannot be empty") - } - - channelID, ok := args[2].(string) - if !ok { - return nil, errors.New("channelID is not a string") - } - if channelID == "" { - return nil, errors.New("channelID cannot be empty") - } - - denom := args[3].(string) - if denom == "" { - return nil, errors.New("invalid denom") - } - - amount, ok := args[4].(*big.Int) - if !ok { - return nil, errors.New("amount is not a big.Int") - } - return &ValidatedArgs{ - senderSeiAddr: senderSeiAddr, - receiverAddressString: receiverAddressString, - port: port, - channelID: channelID, - denom: denom, - amount: amount, - }, nil -} - -func addMemo(memoArg interface{}, transferMsg types.MsgTransfer) types.MsgTransfer { - memo := "" - if memoArg != nil { - memo = memoArg.(string) - } - transferMsg.Memo = memo - return transferMsg -} diff --git a/precompiles/ibc/retired b/precompiles/ibc/retired new file mode 100644 index 0000000000..4c9328dbb9 --- /dev/null +++ b/precompiles/ibc/retired @@ -0,0 +1 @@ +This marker keeps scripts/bump_version from archiving or regenerating this retired precompile. diff --git a/precompiles/ibc/setup.go b/precompiles/ibc/setup.go index eb7963fb89..b8a0448e8f 100644 --- a/precompiles/ibc/setup.go +++ b/precompiles/ibc/setup.go @@ -1,49 +1,42 @@ -// Code generated by scripts/bump_version; DO NOT EDIT. - package ibc import ( - "github.com/ethereum/go-ethereum/core/vm" - ibcv552 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v552" - ibcv555 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v555" - ibcv562 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v562" - ibcv580 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v580" - ibcv601 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v601" - ibcv603 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v603" - ibcv605 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v605" - ibcv606 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v606" - ibcv610 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v610" - ibcv614 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v614" - ibcv620 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v620" - ibcv630 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v630" - ibcv640 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v640" - ibcv65 "github.com/sei-protocol/sei-chain/precompiles/ibc/legacy/v65" + "embed" + + pcommon "github.com/sei-protocol/sei-chain/precompiles/common" "github.com/sei-protocol/sei-chain/precompiles/utils" ) -func GetVersioned(latestUpgrade string, keepers utils.Keepers) utils.VersionedPrecompiles { - return utils.VersionedPrecompiles{ - latestUpgrade: check(NewPrecompile(keepers)), - "v5.5.2": check(ibcv552.NewPrecompile(keepers)), - "v5.5.5": check(ibcv555.NewPrecompile(keepers)), - "v5.6.2": check(ibcv562.NewPrecompile(keepers)), - "v5.8.0": check(ibcv580.NewPrecompile(keepers)), - "v6.0.1": check(ibcv601.NewPrecompile(keepers)), - "v6.0.3": check(ibcv603.NewPrecompile(keepers)), - "v6.0.5": check(ibcv605.NewPrecompile(keepers)), - "v6.0.6": check(ibcv606.NewPrecompile(keepers)), - "v6.1.0": check(ibcv610.NewPrecompile(keepers)), - "v6.1.4": check(ibcv614.NewPrecompile(keepers)), - "v6.2.0": check(ibcv620.NewPrecompile(keepers)), - "v6.3.0": check(ibcv630.NewPrecompile(keepers)), - "v6.4.0": check(ibcv640.NewPrecompile(keepers)), - "v6.5": check(ibcv65.NewPrecompile(keepers)), - } +// The retired precompile still uses the ABI active at the traced height so +// historical method selectors are decoded before returning the retirement +// reason. Several releases share the same ABI, but keeping the files at their +// archived paths makes the version mapping explicit and mechanically checkable. +// +//go:embed legacy/*/abi.json +var legacyABIs embed.FS + +var legacyABIByVersion = map[string]string{ + "v5.5.2": "legacy/v552/abi.json", + "v5.5.5": "legacy/v555/abi.json", + "v5.6.2": "legacy/v562/abi.json", + "v5.8.0": "legacy/v580/abi.json", + "v6.0.1": "legacy/v601/abi.json", + "v6.0.3": "legacy/v603/abi.json", + "v6.0.5": "legacy/v605/abi.json", + "v6.0.6": "legacy/v606/abi.json", + "v6.1.0": "legacy/v610/abi.json", + "v6.1.4": "legacy/v614/abi.json", + "v6.2.0": "legacy/v620/abi.json", + "v6.3.0": "legacy/v630/abi.json", + "v6.4.0": "legacy/v640/abi.json", + "v6.5": "legacy/v65/abi.json", } -func check(p vm.PrecompiledContract, err error) vm.PrecompiledContract { - if err != nil { - panic(err) +func GetVersioned(latestUpgrade string, keepers utils.Keepers) utils.VersionedPrecompiles { + versioned := make(utils.VersionedPrecompiles, len(legacyABIByVersion)+1) + versioned[latestUpgrade] = newRetiredPrecompile(pcommon.MustGetABI(currentABI, "abi.json"), keepers) + for version, filename := range legacyABIByVersion { + versioned[version] = newRetiredPrecompile(pcommon.MustGetABI(legacyABIs, filename), keepers) } - return p + return versioned } diff --git a/precompiles/utils/expected_keepers.go b/precompiles/utils/expected_keepers.go index ab0581c70e..3934b58f2d 100644 --- a/precompiles/utils/expected_keepers.go +++ b/precompiles/utils/expected_keepers.go @@ -4,10 +4,6 @@ import ( "context" "math/big" - connectiontypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/03-connection/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/04-channel/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/exported" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/sei-protocol/sei-chain/sei-cosmos/client" @@ -24,8 +20,6 @@ import ( slashingtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/slashing/types" stakingtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/staking/types" upgradetypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/upgrade/types" - ibctypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" "github.com/sei-protocol/sei-chain/utils" minttypes "github.com/sei-protocol/sei-chain/x/mint/types" oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" @@ -57,10 +51,6 @@ type Keepers interface { SlashingMS() SlashingMsgServer SlashingQ() SlashingQuerier UpgradeQ() UpgradeQuerier - TransferK() TransferKeeper - ClientK() ClientKeeper - ConnectionK() ConnectionKeeper - ChannelK() ChannelKeeper TxConfig() client.TxConfig Codec() codec.Codec } @@ -94,10 +84,6 @@ func (ek *EmptyKeepers) ParamsQ() ParamsQuerier { return nil } func (ek *EmptyKeepers) SlashingMS() SlashingMsgServer { return nil } func (ek *EmptyKeepers) SlashingQ() SlashingQuerier { return nil } func (ek *EmptyKeepers) UpgradeQ() UpgradeQuerier { return nil } -func (ek *EmptyKeepers) TransferK() TransferKeeper { return nil } -func (ek *EmptyKeepers) ClientK() ClientKeeper { return nil } -func (ek *EmptyKeepers) ConnectionK() ConnectionKeeper { return nil } -func (ek *EmptyKeepers) ChannelK() ChannelKeeper { return nil } func (ek *EmptyKeepers) TxConfig() client.TxConfig { return nil } func (ek *EmptyKeepers) Codec() codec.Codec { return nil } @@ -232,33 +218,6 @@ type DistributionKeeper interface { DelegationTotalRewards(c context.Context, req *distrtypes.QueryDelegationTotalRewardsRequest) (*distrtypes.QueryDelegationTotalRewardsResponse, error) } -type TransferKeeper interface { - Transfer(goCtx context.Context, msg *ibctypes.MsgTransfer) (*ibctypes.MsgTransferResponse, error) - SendTransfer( - ctx sdk.Context, - sourcePort, - sourceChannel string, - token sdk.Coin, - sender sdk.AccAddress, - receiver string, - timeoutHeight clienttypes.Height, - timeoutTimestamp uint64, - ) error -} - -type ClientKeeper interface { - GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) - GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) -} - -type ConnectionKeeper interface { - GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) -} - -type ChannelKeeper interface { - GetChannel(ctx sdk.Context, portID, channelID string) (types.Channel, bool) -} - type BankQuerier interface { SpendableBalances(ctx context.Context, req *banktypes.QuerySpendableBalancesRequest) (*banktypes.QuerySpendableBalancesResponse, error) TotalSupply(ctx context.Context, req *banktypes.QueryTotalSupplyRequest) (*banktypes.QueryTotalSupplyResponse, error) diff --git a/scripts/bump_version/README.md b/scripts/bump_version/README.md index 009fc27735..0650a6128c 100644 --- a/scripts/bump_version/README.md +++ b/scripts/bump_version/README.md @@ -10,6 +10,10 @@ When a new upgrade version is added to `app/tags`, this tool: 2. Archives their current code into `precompiles//legacy//` 3. Regenerates all `precompiles//setup.go` files from the `versions` files +Modules containing a `retired` marker file are excluded from archival and setup +generation. Their historical version mapping is maintained by the module so a +future upgrade cannot archive or reactivate retired code. + It replaces the old `scripts/bump-version.sh` shell script. ## Developer workflow diff --git a/scripts/bump_version/main.go b/scripts/bump_version/main.go index bcd5251fe0..36628f322d 100644 --- a/scripts/bump_version/main.go +++ b/scripts/bump_version/main.go @@ -46,6 +46,7 @@ const ( tagFile = "app/tags" precompilesDir = "precompiles" commonDir = "precompiles/common" + retiredMarker = "retired" modulePath = "github.com/sei-protocol/sei-chain" commonPkgPath = modulePath + "/precompiles/common" generatedHdr = "// Code generated by scripts/bump_version; DO NOT EDIT." @@ -380,6 +381,9 @@ func regenerateAllSetup() error { moduleName := entry.Name() moduleDir := filepath.Join(precompilesDir, moduleName) + if fileExists(filepath.Join(moduleDir, retiredMarker)) { + continue + } versionsFile := filepath.Join(moduleDir, "versions") if _, err := os.Stat(versionsFile); os.IsNotExist(err) { @@ -515,6 +519,11 @@ func dirExists(path string) bool { return err == nil && info.IsDir() } +func fileExists(path string) bool { + info, err := os.Stat(path) + return err == nil && !info.IsDir() +} + func discoverModules() []string { entries, err := os.ReadDir(precompilesDir) if err != nil { @@ -522,7 +531,7 @@ func discoverModules() []string { } var modules []string for _, e := range entries { - if e.IsDir() && !excludeDirs[e.Name()] { + if e.IsDir() && !excludeDirs[e.Name()] && !fileExists(filepath.Join(precompilesDir, e.Name(), retiredMarker)) { modules = append(modules, e.Name()) } } diff --git a/tools/utils/helper.go b/tools/utils/helper.go index a2af58ecee..c4bdc2e476 100644 --- a/tools/utils/helper.go +++ b/tools/utils/helper.go @@ -16,8 +16,6 @@ import ( slashingtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/slashing/types" stakingtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/staking/types" upgradetypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/upgrade/types" - ibctransfertypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types" - ibchost "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/24-host" "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm" epochmoduletypes "github.com/sei-protocol/sei-chain/x/epoch/types" evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" @@ -29,8 +27,8 @@ import ( var ModuleKeys = sdk.NewKVStoreKeys( authtypes.StoreKey, authzkeeper.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, - evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey, oracletypes.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, + evidencetypes.StoreKey, capabilitytypes.StoreKey, oracletypes.StoreKey, evmtypes.StoreKey, wasm.StoreKey, epochmoduletypes.StoreKey, tokenfactorytypes.StoreKey, ) @@ -45,14 +43,12 @@ var Modules = []string{ "evm", "feegrant", "gov", - "ibc", "mint", "oracle", "params", "slashing", "staking", "tokenfactory", - "transfer", "upgrade", "wasm"} diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index a7371299ee..e1c9ad94bf 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -31,7 +31,6 @@ import ( upgradekeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/upgrade/keeper" receipt "github.com/sei-protocol/sei-chain/sei-db/ledger_db/receipt" sctypes "github.com/sei-protocol/sei-chain/sei-db/state_db/sc/types" - ibctransferkeeper "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/keeper" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" wasmkeeper "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/keeper" @@ -60,7 +59,6 @@ type Keeper struct { bankKeeper bankkeeper.Keeper accountKeeper *authkeeper.AccountKeeper stakingKeeper *stakingkeeper.Keeper - transferKeeper ibctransferkeeper.Keeper wasmKeeper *wasmkeeper.PermissionedKeeper wasmViewKeeper *wasmkeeper.Keeper upgradeKeeper *upgradekeeper.Keeper @@ -132,7 +130,7 @@ func (ctx *ReplayChainContext) Config() *params.ChainConfig { func NewKeeper( storeKey sdk.StoreKey, transientStoreKey sdk.StoreKey, paramstore paramtypes.Subspace, receiptStore receipt.ReceiptStore, bankKeeper bankkeeper.Keeper, accountKeeper *authkeeper.AccountKeeper, stakingKeeper *stakingkeeper.Keeper, - transferKeeper ibctransferkeeper.Keeper, wasmKeeper *wasmkeeper.PermissionedKeeper, wasmViewKeeper *wasmkeeper.Keeper, upgradeKeeper *upgradekeeper.Keeper) *Keeper { + wasmKeeper *wasmkeeper.PermissionedKeeper, wasmViewKeeper *wasmkeeper.Keeper, upgradeKeeper *upgradekeeper.Keeper) *Keeper { if !paramstore.HasKeyTable() { paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) @@ -144,7 +142,6 @@ func NewKeeper( bankKeeper: bankKeeper, accountKeeper: accountKeeper, stakingKeeper: stakingKeeper, - transferKeeper: transferKeeper, wasmKeeper: wasmKeeper, wasmViewKeeper: wasmViewKeeper, upgradeKeeper: upgradeKeeper, From 6d19c7082636b7e787f009f0e4a76b5d7edab5c6 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Sat, 8 Aug 2026 22:06:38 +0100 Subject: [PATCH 2/4] Remove hand maintained versions and categorically fix potential drift --- precompiles/ibc/ibc_test.go | 14 +++++++++-- precompiles/ibc/setup.go | 46 ++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/precompiles/ibc/ibc_test.go b/precompiles/ibc/ibc_test.go index 714a42d700..547f64d05c 100644 --- a/precompiles/ibc/ibc_test.go +++ b/precompiles/ibc/ibc_test.go @@ -2,6 +2,8 @@ package ibc_test import ( "math/big" + "os" + "strings" "testing" "github.com/ethereum/go-ethereum/accounts/abi" @@ -21,8 +23,16 @@ func TestEveryIBCVersionIsRetired(t *testing.T) { ctx := testApp.NewContext(false, tmtypes.Header{}) evm := &vm.EVM{StateDB: state.NewDBImpl(ctx, &testApp.EvmKeeper, true)} - versioned := ibc.GetVersioned("v6.6", testApp.GetPrecompileKeepers()) - require.Len(t, versioned, 15) + manifest, err := os.ReadFile("versions") + require.NoError(t, err) + historicalVersions := strings.Fields(string(manifest)) + + const futureUpgrade = "future-upgrade" + versioned := ibc.GetVersioned(futureUpgrade, testApp.GetPrecompileKeepers()) + require.Len(t, versioned, len(historicalVersions)+1) + for _, version := range historicalVersions { + require.Contains(t, versioned, version) + } for version, contract := range versioned { t.Run(version, func(t *testing.T) { diff --git a/precompiles/ibc/setup.go b/precompiles/ibc/setup.go index b8a0448e8f..dd349247e5 100644 --- a/precompiles/ibc/setup.go +++ b/precompiles/ibc/setup.go @@ -2,41 +2,35 @@ package ibc import ( "embed" + "strings" pcommon "github.com/sei-protocol/sei-chain/precompiles/common" "github.com/sei-protocol/sei-chain/precompiles/utils" ) // The retired precompile still uses the ABI active at the traced height so -// historical method selectors are decoded before returning the retirement -// reason. Several releases share the same ABI, but keeping the files at their -// archived paths makes the version mapping explicit and mechanically checkable. +// historical method selectors are decoded before returning the retirement reason. +// The versions manifest is frozen with the module and remains the source of truth +// for every ABI that must be available after future upgrades. // -//go:embed legacy/*/abi.json -var legacyABIs embed.FS - -var legacyABIByVersion = map[string]string{ - "v5.5.2": "legacy/v552/abi.json", - "v5.5.5": "legacy/v555/abi.json", - "v5.6.2": "legacy/v562/abi.json", - "v5.8.0": "legacy/v580/abi.json", - "v6.0.1": "legacy/v601/abi.json", - "v6.0.3": "legacy/v603/abi.json", - "v6.0.5": "legacy/v605/abi.json", - "v6.0.6": "legacy/v606/abi.json", - "v6.1.0": "legacy/v610/abi.json", - "v6.1.4": "legacy/v614/abi.json", - "v6.2.0": "legacy/v620/abi.json", - "v6.3.0": "legacy/v630/abi.json", - "v6.4.0": "legacy/v640/abi.json", - "v6.5": "legacy/v65/abi.json", -} +//go:embed versions legacy/*/abi.json +var retiredAssets embed.FS func GetVersioned(latestUpgrade string, keepers utils.Keepers) utils.VersionedPrecompiles { - versioned := make(utils.VersionedPrecompiles, len(legacyABIByVersion)+1) - versioned[latestUpgrade] = newRetiredPrecompile(pcommon.MustGetABI(currentABI, "abi.json"), keepers) - for version, filename := range legacyABIByVersion { - versioned[version] = newRetiredPrecompile(pcommon.MustGetABI(legacyABIs, filename), keepers) + historicalVersions := getHistoricalVersions() + versioned := make(utils.VersionedPrecompiles, len(historicalVersions)+1) + for _, version := range historicalVersions { + filename := "legacy/" + strings.ReplaceAll(version, ".", "") + "/abi.json" + versioned[version] = newRetiredPrecompile(pcommon.MustGetABI(retiredAssets, filename), keepers) } + versioned[latestUpgrade] = newRetiredPrecompile(pcommon.MustGetABI(currentABI, "abi.json"), keepers) return versioned } + +func getHistoricalVersions() []string { + manifest, err := retiredAssets.ReadFile("versions") + if err != nil { + panic(err) + } + return strings.Fields(string(manifest)) +} From bd7abc84a7bd14464d5ccfe268599c021a7954e6 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 10 Aug 2026 11:46:54 +0100 Subject: [PATCH 3/4] Reflect on review comments --- precompiles/common/retired.go | 4 ++-- precompiles/ibc/ibc_test.go | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/precompiles/common/retired.go b/precompiles/common/retired.go index ccb1a2aada..78929b7383 100644 --- a/precompiles/common/retired.go +++ b/precompiles/common/retired.go @@ -32,9 +32,9 @@ type retiredExecutor struct { func (e *retiredExecutor) Execute(ctx sdk.Context, _ *abi.Method, _ common.Address, _ common.Address, _ []interface{}, value *big.Int, _ bool, _ *vm.EVM, _ uint64, _ *tracing.Hooks) ([]byte, uint64, error) { if err := ValidateNonPayable(value); err != nil { - return nil, 0, err + return common.CopyBytes(e.revertData), 0, err } - return e.revertData, GetRemainingGas(ctx, e.evmKeeper), e.err + return common.CopyBytes(e.revertData), GetRemainingGas(ctx, e.evmKeeper), e.err } func (e *retiredExecutor) EVMKeeper() putils.EVMKeeper { diff --git a/precompiles/ibc/ibc_test.go b/precompiles/ibc/ibc_test.go index 547f64d05c..778f6cfb45 100644 --- a/precompiles/ibc/ibc_test.go +++ b/precompiles/ibc/ibc_test.go @@ -68,7 +68,7 @@ func TestRetiredIBCPrecompileRemainsNonPayable(t *testing.T) { precompile, err := ibc.NewPrecompile(testApp.GetPrecompileKeepers()) require.NoError(t, err) - ret, _, err := precompile.RunAndCalculateGas( + ret, remainingGas, err := precompile.RunAndCalculateGas( evm, common.Address{}, common.Address{}, @@ -80,7 +80,11 @@ func TestRetiredIBCPrecompileRemainsNonPayable(t *testing.T) { false, ) require.ErrorIs(t, err, vm.ErrExecutionReverted) - require.Empty(t, ret) + require.Zero(t, remainingGas) + + reason, err := abi.UnpackRevert(ret) + require.NoError(t, err) + require.Equal(t, ibc.RetiredReason, reason) } func validCallData(t *testing.T, contractABI abi.ABI) []byte { From a30958f0c5908eac950d1d0dd16ac1c4c0ecd129 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 10 Aug 2026 15:51:09 +0100 Subject: [PATCH 4/4] Error hard instead of scilently reverting on unsuported IBC traces --- evmrpc/historical_trace_error.go | 57 +++++++++++++++++++++++++ evmrpc/historical_trace_error_test.go | 43 +++++++++++++++++++ evmrpc/simulate.go | 6 ++- evmrpc/trace_baker.go | 17 +++++++- evmrpc/trace_profile.go | 7 +++ evmrpc/tracers.go | 17 ++++++-- precompiles/common/retired.go | 61 +++++++++++++++++++++++++-- precompiles/ibc/ibc.go | 9 +++- precompiles/ibc/ibc_test.go | 57 +++++++++++++++++++++++++ 9 files changed, 263 insertions(+), 11 deletions(-) create mode 100644 evmrpc/historical_trace_error.go create mode 100644 evmrpc/historical_trace_error_test.go diff --git a/evmrpc/historical_trace_error.go b/evmrpc/historical_trace_error.go new file mode 100644 index 0000000000..2a33cde766 --- /dev/null +++ b/evmrpc/historical_trace_error.go @@ -0,0 +1,57 @@ +package evmrpc + +import ( + "context" + "sync" + + pcommon "github.com/sei-protocol/sei-chain/precompiles/common" + sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" +) + +type historicalTraceErrorCollector struct { + mu sync.Mutex + err error +} + +func (c *historicalTraceErrorCollector) RecordHistoricalTraceError(err error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.err = err + } +} + +func (c *historicalTraceErrorCollector) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +type historicalTraceErrorCollectorKey struct{} + +func withHistoricalTraceErrorCollector(ctx context.Context) (context.Context, *historicalTraceErrorCollector) { + collector := &historicalTraceErrorCollector{} + return context.WithValue(ctx, historicalTraceErrorCollectorKey{}, collector), collector +} + +func historicalTraceErrorCollectorFromContext(ctx context.Context) *historicalTraceErrorCollector { + collector, _ := ctx.Value(historicalTraceErrorCollectorKey{}).(*historicalTraceErrorCollector) + return collector +} + +// attachHistoricalTraceErrorCollector bridges the RPC request context into the +// SDK context used by precompiles during replay. +func attachHistoricalTraceErrorCollector(sdkCtx sdk.Context, requestCtx context.Context) sdk.Context { + collector := historicalTraceErrorCollectorFromContext(requestCtx) + if collector == nil { + return sdkCtx + } + return pcommon.WithHistoricalTraceErrorRecorder(sdkCtx, collector) +} + +func rejectUnavailableHistoricalTrace(result interface{}, traceErr error, collector *historicalTraceErrorCollector) (interface{}, error) { + if err := collector.Err(); err != nil { + return nil, err + } + return result, traceErr +} diff --git a/evmrpc/historical_trace_error_test.go b/evmrpc/historical_trace_error_test.go new file mode 100644 index 0000000000..4cf1a9042d --- /dev/null +++ b/evmrpc/historical_trace_error_test.go @@ -0,0 +1,43 @@ +package evmrpc + +import ( + "context" + "errors" + "testing" + + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/require" +) + +func TestRejectUnavailableHistoricalTrace(t *testing.T) { + ctx, collector := withHistoricalTraceErrorCollector(context.Background()) + require.NotNil(t, historicalTraceErrorCollectorFromContext(ctx)) + + historicalErr := errors.New("historical execution unavailable") + collector.RecordHistoricalTraceError(historicalErr) + + result, err := rejectUnavailableHistoricalTrace("plausible but wrong", nil, collector) + require.Nil(t, result) + require.ErrorIs(t, err, historicalErr) +} + +type recordingHistoricalTraceBlockTracer struct { + err error +} + +func (t recordingHistoricalTraceBlockTracer) TraceBlockByNumber(ctx context.Context, _ rpc.BlockNumber, _ *tracers.TraceConfig) ([]*tracers.TxTraceResult, error) { + historicalTraceErrorCollectorFromContext(ctx).RecordHistoricalTraceError(t.err) + return []*tracers.TxTraceResult{{}}, nil +} + +func TestHistoricalTraceGuardedBlockTracer(t *testing.T) { + historicalErr := errors.New("historical execution unavailable") + tracer := historicalTraceGuardedBlockTracer{ + delegate: recordingHistoricalTraceBlockTracer{err: historicalErr}, + } + + result, err := tracer.TraceBlockByNumber(context.Background(), 1, nil) + require.Nil(t, result) + require.ErrorIs(t, err, historicalErr) +} diff --git a/evmrpc/simulate.go b/evmrpc/simulate.go index dc05696a2c..8f7cc9387c 100644 --- a/evmrpc/simulate.go +++ b/evmrpc/simulate.go @@ -701,6 +701,7 @@ func (b *Backend) initializeBlock(ctx context.Context, block *ethtypes.Block, ct reqBeginBlock := tmBlock.Block.ToReqBeginBlock(res.Validators) reqBeginBlock.Simulate = true baseCtx, baseRelease := ctxProvider(prevBlockHeight) + baseCtx = attachHistoricalTraceErrorCollector(baseCtx, ctx) sdkCtx := baseCtx.WithBlockHeight(blockNumber).WithBlockTime(tmBlock.Block.Time) legacyabci.BeginBlock(sdkCtx, blockNumber, reqBeginBlock.LastCommitInfo.Votes, tmBlock.Block.Evidence.ToABCI(), b.beginBlockKeepers) nextCtx, nextRelease := ctxProvider(sdkCtx.BlockHeight()) @@ -714,7 +715,10 @@ func (b *Backend) initializeBlock(ctx context.Context, block *ethtypes.Block, ct }, nil } -func (b *Backend) GetEVM(_ context.Context, msg *core.Message, stateDB vm.StateDB, h *ethtypes.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { +func (b *Backend) GetEVM(ctx context.Context, msg *core.Message, stateDB vm.StateDB, h *ethtypes.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { + if db := state.GetDBImpl(stateDB); db != nil { + db.WithCtx(attachHistoricalTraceErrorCollector(db.Ctx(), ctx)) + } txContext := core.NewEVMTxContext(msg) if blockCtx == nil { blockCtx, _ = b.keeper.GetVMBlockContext(b.ctxProvider(LatestCtxHeight).WithIsEVM(true).WithEVMEntryViaWasmdPrecompile(wasmd.IsWasmdCall(msg.To)), b.keeper.GetGasPool()) diff --git a/evmrpc/trace_baker.go b/evmrpc/trace_baker.go index 4cfe03a0b4..24e72bd056 100644 --- a/evmrpc/trace_baker.go +++ b/evmrpc/trace_baker.go @@ -21,6 +21,19 @@ type blockTracer interface { TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *gethtracers.TraceConfig) ([]*gethtracers.TxTraceResult, error) } +type historicalTraceGuardedBlockTracer struct { + delegate blockTracer +} + +func (t historicalTraceGuardedBlockTracer) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *gethtracers.TraceConfig) ([]*gethtracers.TxTraceResult, error) { + ctx, collector := withHistoricalTraceErrorCollector(ctx) + result, err := t.delegate.TraceBlockByNumber(ctx, number, config) + if historicalErr := collector.Err(); historicalErr != nil { + return nil, historicalErr + } + return result, err +} + // TraceBaker re-runs committed blocks through the tracer in background workers // and writes the JSON to a TraceDB. Enqueue is non-blocking; misses fall // through to live re-execution. @@ -64,13 +77,13 @@ func StartTraceBakerForDebugAPI(api *DebugAPI, cfg TraceBakerConfig) *TraceBaker if cache == nil { return nil } - b := NewTraceBaker(api.tracersAPI, cache, cfg) + b := NewTraceBaker(historicalTraceGuardedBlockTracer{delegate: api.tracersAPI}, cache, cfg) cache.SetTraceEnqueuer(b) b.Start() return b } -func NewTraceBaker(api *gethtracers.API, cache *keeper.TraceDB, cfg TraceBakerConfig) *TraceBaker { +func NewTraceBaker(api blockTracer, cache *keeper.TraceDB, cfg TraceBakerConfig) *TraceBaker { if cfg.Workers <= 0 { cfg.Workers = 1 } diff --git a/evmrpc/trace_profile.go b/evmrpc/trace_profile.go index 244836c5c6..929dcadd38 100644 --- a/evmrpc/trace_profile.go +++ b/evmrpc/trace_profile.go @@ -55,6 +55,7 @@ func (api *DebugAPI) TraceTransactionProfile(ctx context.Context, hash common.Ha return nil, returnErr } + ctx, collector := withHistoricalTraceErrorCollector(ctx) ctx, done, err := api.prepareTraceContext(ctx) if err != nil { return nil, err @@ -94,6 +95,9 @@ func (api *DebugAPI) TraceTransactionProfile(ctx context.Context, hash common.Ha if err != nil { return nil, err } + if err := collector.Err(); err != nil { + return nil, err + } blockContextStart := time.Now() blockCtx, err := tracingBackend.GetBlockContext(ctx, block, statedb, tracingBackend) @@ -119,6 +123,9 @@ func (api *DebugAPI) TraceTransactionProfile(ctx context.Context, hash common.Ha if err != nil { return nil, err } + if err := collector.Err(); err != nil { + return nil, err + } storeDump := dumpStoreTrace(statedb) historicalLookupNanos := historicalLookupNanos(storeDump) diff --git a/evmrpc/tracers.go b/evmrpc/tracers.go index 9afaf9ccfc..85812a9dfb 100644 --- a/evmrpc/tracers.go +++ b/evmrpc/tracers.go @@ -339,6 +339,7 @@ func (api *DebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, con return cached, nil } + ctx, collector := withHistoricalTraceErrorCollector(ctx) ctx, done, err := api.prepareTraceContext(ctx) if err != nil { return nil, err @@ -349,7 +350,8 @@ func (api *DebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, con config = &tracers.TraceConfig{} } api.clampDefaultStructLogLimit(config) - return api.tracersAPI.TraceTransaction(ctx, hash, config) + result, returnErr = api.tracersAPI.TraceTransaction(ctx, hash, config) + return rejectUnavailableHistoricalTrace(result, returnErr, collector) } func (api *DebugAPI) tryTraceCache(hash common.Hash, config *tracers.TraceConfig) (interface{}, bool) { @@ -558,6 +560,7 @@ func (api *DebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNum return cached, nil } + ctx, collector := withHistoricalTraceErrorCollector(ctx) if config == nil { config = &tracers.TraceConfig{} } @@ -567,7 +570,7 @@ func (api *DebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNum } else { result, returnErr = api.tracersAPI.TraceBlockByNumber(ctx, number, config) } - return + return rejectUnavailableHistoricalTrace(result, returnErr, collector) } func (api *DebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *tracers.TraceConfig) (result interface{}, returnErr error) { @@ -594,6 +597,7 @@ func (api *DebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, con return cached, nil } + ctx, collector := withHistoricalTraceErrorCollector(ctx) if config == nil { config = &tracers.TraceConfig{} } @@ -603,7 +607,7 @@ func (api *DebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, con } else { result, returnErr = api.tracersAPI.TraceBlockByHash(ctx, hash, config) } - return + return rejectUnavailableHistoricalTrace(result, returnErr, collector) } func (api *DebugAPI) TraceCall(ctx context.Context, args export.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *tracers.TraceCallConfig) (result interface{}, returnErr error) { @@ -633,8 +637,9 @@ func (api *DebugAPI) TraceCall(ctx context.Context, args export.TransactionArgs, return nil, returnErr } api.clampDefaultStructLogLimit(&config.TraceConfig) + ctx, collector := withHistoricalTraceErrorCollector(ctx) result, returnErr = api.tracersAPI.TraceCall(ctx, args, blockNrOrHash, config) - return + return rejectUnavailableHistoricalTrace(result, returnErr, collector) } func (api *DebugAPI) GetRawHeader(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (_ hexutil.Bytes, returnErr error) { @@ -687,6 +692,7 @@ func (api *DebugAPI) TraceStateAccess(ctx context.Context, hash common.Hash) (re return nil, returnErr } + ctx, collector := withHistoricalTraceErrorCollector(ctx) ctx, done, err := api.prepareTraceContext(ctx) if err != nil { return nil, err @@ -721,6 +727,9 @@ func (api *DebugAPI) TraceStateAccess(ctx context.Context, hash common.Hash) (re if err != nil { return nil, err } + if err := collector.Err(); err != nil { + return nil, err + } // Bail before the potentially expensive prestate/trace serialization if the // trace deadline has already elapsed during replay. if err := ctx.Err(); err != nil { diff --git a/precompiles/common/retired.go b/precompiles/common/retired.go index 78929b7383..c362ac7bd7 100644 --- a/precompiles/common/retired.go +++ b/precompiles/common/retired.go @@ -1,7 +1,9 @@ package common import ( + "context" "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum/accounts/abi" @@ -25,12 +27,17 @@ func NewRetiredPrecompile(a abi.ABI, address common.Address, name string, evmKee } type retiredExecutor struct { - evmKeeper putils.EVMKeeper - err error - revertData []byte + evmKeeper putils.EVMKeeper + err error + revertData []byte + historicalTraceUnsupportedAt func(sdk.Context) bool + name string } func (e *retiredExecutor) Execute(ctx sdk.Context, _ *abi.Method, _ common.Address, _ common.Address, _ []interface{}, value *big.Int, _ bool, _ *vm.EVM, _ uint64, _ *tracing.Hooks) ([]byte, uint64, error) { + if e.historicalTraceUnsupportedAt != nil && e.historicalTraceUnsupportedAt(ctx) { + recordHistoricalTraceError(ctx, &HistoricalTraceUnavailableError{Precompile: e.name}) + } if err := ValidateNonPayable(value); err != nil { return common.CopyBytes(e.revertData), 0, err } @@ -41,6 +48,54 @@ func (e *retiredExecutor) EVMKeeper() putils.EVMKeeper { return e.evmKeeper } +// NewRetiredPrecompileWithTraceGuard builds a retired precompile whose valid +// calls mark selected trace contexts as unsupported. The EVM still receives a +// normal revert internally; the RPC replay boundary turns the marker into a +// hard error. +func NewRetiredPrecompileWithTraceGuard( + a abi.ABI, + address common.Address, + name string, + evmKeeper putils.EVMKeeper, + reason string, + historicalTraceUnsupportedAt func(sdk.Context) bool, +) *DynamicGasPrecompile { + p := NewRetiredPrecompile(a, address, name, evmKeeper, reason) + executor := p.GetExecutor().(*retiredExecutor) + executor.historicalTraceUnsupportedAt = historicalTraceUnsupportedAt + executor.name = name + return p +} + +// HistoricalTraceUnavailableError means replay reached precompile code that is +// no longer available and must not return a plausible but incorrect trace. +type HistoricalTraceUnavailableError struct { + Precompile string +} + +func (e *HistoricalTraceUnavailableError) Error() string { + return fmt.Sprintf("historical trace unavailable: %s precompile implementation has been retired", e.Precompile) +} + +type HistoricalTraceErrorRecorder interface { + RecordHistoricalTraceError(error) +} + +type historicalTraceErrorRecorderKey struct{} + +// WithHistoricalTraceErrorRecorder installs the trace-only error side channel +// used to carry incompatibility beyond the EVM's normal revert boundary. +func WithHistoricalTraceErrorRecorder(ctx sdk.Context, recorder HistoricalTraceErrorRecorder) sdk.Context { + return ctx.WithContext(context.WithValue(ctx.Context(), historicalTraceErrorRecorderKey{}, recorder)) +} + +func recordHistoricalTraceError(ctx sdk.Context, err error) { + recorder, ok := ctx.Context().Value(historicalTraceErrorRecorderKey{}).(HistoricalTraceErrorRecorder) + if ok { + recorder.RecordHistoricalTraceError(err) + } +} + func encodeRevertReason(reason string) []byte { stringType, err := abi.NewType("string", "", nil) if err != nil { diff --git a/precompiles/ibc/ibc.go b/precompiles/ibc/ibc.go index 76e3adda14..73cb422e0a 100644 --- a/precompiles/ibc/ibc.go +++ b/precompiles/ibc/ibc.go @@ -7,12 +7,16 @@ import ( "github.com/ethereum/go-ethereum/common" pcommon "github.com/sei-protocol/sei-chain/precompiles/common" "github.com/sei-protocol/sei-chain/precompiles/utils" + sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" + "golang.org/x/mod/semver" ) const IBCAddress = "0x0000000000000000000000000000000000001009" const RetiredReason = "ibc precompile is retired; ibc transfers are disabled" +const RetirementUpgrade = "v6.7" + //go:embed abi.json var currentABI embed.FS @@ -23,11 +27,14 @@ func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) } func newRetiredPrecompile(a abi.ABI, keepers utils.Keepers) *pcommon.DynamicGasPrecompile { - return pcommon.NewRetiredPrecompile( + return pcommon.NewRetiredPrecompileWithTraceGuard( a, common.HexToAddress(IBCAddress), "ibc", keepers.EVMK(), RetiredReason, + func(ctx sdk.Context) bool { + return ctx.IsTracing() && semver.Compare(ctx.ClosestUpgradeName(), RetirementUpgrade) < 0 + }, ) } diff --git a/precompiles/ibc/ibc_test.go b/precompiles/ibc/ibc_test.go index 778f6cfb45..f840096309 100644 --- a/precompiles/ibc/ibc_test.go +++ b/precompiles/ibc/ibc_test.go @@ -18,6 +18,14 @@ import ( "github.com/sei-protocol/sei-chain/x/evm/state" ) +type historicalTraceErrorRecorder struct { + err error +} + +func (r *historicalTraceErrorRecorder) RecordHistoricalTraceError(err error) { + r.err = err +} + func TestEveryIBCVersionIsRetired(t *testing.T) { testApp := app.Setup(t, false, true, false) ctx := testApp.NewContext(false, tmtypes.Header{}) @@ -87,6 +95,55 @@ func TestRetiredIBCPrecompileRemainsNonPayable(t *testing.T) { require.Equal(t, ibc.RetiredReason, reason) } +func TestHistoricalIBCTraceRecordsHardFailure(t *testing.T) { + testApp := app.Setup(t, false, true, false) + recorder := &historicalTraceErrorRecorder{} + historicalCtx := pcommon.WithHistoricalTraceErrorRecorder( + testApp.NewContext(false, tmtypes.Header{}). + WithIsTracing(true). + WithClosestUpgradeName("v6.6"), + recorder, + ) + stateDB := state.NewDBImpl(historicalCtx, &testApp.EvmKeeper, true) + evm := &vm.EVM{StateDB: stateDB} + + precompile, err := ibc.NewPrecompile(testApp.GetPrecompileKeepers()) + require.NoError(t, err) + _, _, err = precompile.RunAndCalculateGas( + evm, + common.Address{}, + common.Address{}, + validCallData(t, precompile.GetABI()), + 1_000_000, + nil, + nil, + false, + false, + ) + require.ErrorIs(t, err, vm.ErrExecutionReverted) + var unavailable *pcommon.HistoricalTraceUnavailableError + require.ErrorAs(t, recorder.err, &unavailable) + + recorder.err = nil + stateDB.WithCtx(pcommon.WithHistoricalTraceErrorRecorder( + historicalCtx.WithClosestUpgradeName(ibc.RetirementUpgrade), + recorder, + )) + _, _, err = precompile.RunAndCalculateGas( + evm, + common.Address{}, + common.Address{}, + validCallData(t, precompile.GetABI()), + 1_000_000, + nil, + nil, + false, + false, + ) + require.ErrorIs(t, err, vm.ErrExecutionReverted) + require.NoError(t, recorder.err) +} + func validCallData(t *testing.T, contractABI abi.ABI) []byte { t.Helper()