diff --git a/app/app.go b/app/app.go index aaebe1645..5a5e71871 100644 --- a/app/app.go +++ b/app/app.go @@ -563,7 +563,7 @@ func New( app.BankKeeper, scopedTransferKeeper, app.FeeKeeper, - app.ContractManagerKeeper, + contractmanager.NewSudoLimitWrapper(app.ContractManagerKeeper, &app.WasmKeeper), ) app.RouterKeeper.SetTransferKeeper(app.TransferKeeper.Keeper) @@ -660,8 +660,10 @@ func New( memKeys[interchaintxstypes.MemStoreKey], app.IBCKeeper.ChannelKeeper, app.ICAControllerKeeper, - app.ContractManagerKeeper, + contractmanager.NewSudoLimitWrapper(app.ContractManagerKeeper, &app.WasmKeeper), app.FeeKeeper, + app.BankKeeper, + app.FeeBurnerKeeper, ) app.CronKeeper = *cronkeeper.NewKeeper(appCodec, keys[crontypes.StoreKey], keys[crontypes.MemStoreKey], app.AccountKeeper) @@ -699,7 +701,10 @@ func New( if len(enabledProposals) != 0 { app.AdminmoduleKeeper.Router().AddRoute(wasm.RouterKey, wasm.NewWasmProposalHandler(app.WasmKeeper, enabledProposals)) } - transferIBCModule := transferSudo.NewIBCModule(app.TransferKeeper) + transferIBCModule := transferSudo.NewIBCModule( + app.TransferKeeper, + contractmanager.NewSudoLimitWrapper(app.ContractManagerKeeper, &app.WasmKeeper), + ) // receive call order: wasmHooks#OnRecvPacketOverride(transferIbcModule#OnRecvPacket()) ibcHooksMiddleware := ibchooks.NewIBCMiddleware(&transferIBCModule, &app.HooksICS4Wrapper) app.HooksTransferIBCModule = &ibcHooksMiddleware diff --git a/app/upgrades/sdk47/upgrades.go b/app/upgrades/sdk47/upgrades.go index f0cfaa7e3..54529d9d6 100644 --- a/app/upgrades/sdk47/upgrades.go +++ b/app/upgrades/sdk47/upgrades.go @@ -135,7 +135,7 @@ func migrateCronParams(ctx sdk.Context, paramsKeepers paramskeeper.Keeper, store func migrateFeeRefunderParams(ctx sdk.Context, paramsKeepers paramskeeper.Keeper, storeKey storetypes.StoreKey, codec codec.Codec) error { store := ctx.KVStore(storeKey) var currParams feerefundertypes.Params - subspace, _ := paramsKeepers.GetSubspace(crontypes.StoreKey) + subspace, _ := paramsKeepers.GetSubspace(feerefundertypes.StoreKey) subspace.GetParamSet(ctx, &currParams) if err := currParams.Validate(); err != nil { @@ -197,6 +197,7 @@ func migrateInterchainTxsParams(ctx sdk.Context, paramsKeepers paramskeeper.Keep var currParams interchaintxstypes.Params subspace, _ := paramsKeepers.GetSubspace(interchaintxstypes.StoreKey) subspace.GetParamSet(ctx, &currParams) + currParams.RegisterFee = interchaintxstypes.DefaultRegisterFee if err := currParams.Validate(); err != nil { return err diff --git a/neutronutils/errors/errors.go b/neutronutils/errors/errors.go deleted file mode 100644 index 9dd7574d9..000000000 --- a/neutronutils/errors/errors.go +++ /dev/null @@ -1,21 +0,0 @@ -package errors - -import ( - "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// OutOfGasRecovery converts `out of gas` panic into an error -// leaving unprocessed any other kinds of panics -func OutOfGasRecovery( - gasMeter sdk.GasMeter, - err *error, -) { - if r := recover(); r != nil { - _, ok := r.(sdk.ErrorOutOfGas) - if !ok || !gasMeter.IsOutOfGas() { - panic(r) - } - *err = errors.Wrapf(errors.ErrPanic, "%v", r) - } -} diff --git a/neutronutils/utils.go b/neutronutils/utils.go deleted file mode 100644 index 2308c2a0f..000000000 --- a/neutronutils/utils.go +++ /dev/null @@ -1,11 +0,0 @@ -package neutronutils - -import sdk "github.com/cosmos/cosmos-sdk/types" - -// CreateCachedContext creates a cached context with a limited gas meter. -func CreateCachedContext(ctx sdk.Context, gasLimit uint64) (sdk.Context, func()) { - cacheCtx, writeFn := ctx.CacheContext() - gasMeter := sdk.NewGasMeter(gasLimit) - cacheCtx = cacheCtx.WithGasMeter(gasMeter) - return cacheCtx, writeFn -} diff --git a/proto/neutron/contractmanager/failure.proto b/proto/neutron/contractmanager/failure.proto index 2ac24a5a6..d18386884 100644 --- a/proto/neutron/contractmanager/failure.proto +++ b/proto/neutron/contractmanager/failure.proto @@ -13,10 +13,6 @@ message Failure { string address = 1; // Id of the failure under specific address uint64 id = 2; - // Acknowledgement type - string ack_type = 3; - // IBC Packet - ibc.core.channel.v1.Packet packet = 4; - // Acknowledgement - ibc.core.channel.v1.Acknowledgement ack = 5; + // Serialized MessageSudoCallback with Packet and Ack(if exists) + bytes sudo_payload = 3; } diff --git a/proto/neutron/interchaintxs/v1/params.proto b/proto/neutron/interchaintxs/v1/params.proto index c25893eef..734e7cb2e 100644 --- a/proto/neutron/interchaintxs/v1/params.proto +++ b/proto/neutron/interchaintxs/v1/params.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package neutron.interchaintxs; import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; option go_package = "github.com/neutron-org/neutron/x/interchaintxs/types"; @@ -10,4 +11,6 @@ message Params { option (gogoproto.goproto_stringer) = false; // Defines maximum amount of messages to be passed in MsgSubmitTx uint64 msg_submit_tx_max_messages = 1; + // Defines a minimum fee required to register interchain account + repeated cosmos.base.v1beta1.Coin register_fee = 2 [ (gogoproto.nullable) = false ]; } diff --git a/proto/neutron/interchaintxs/v1/tx.proto b/proto/neutron/interchaintxs/v1/tx.proto index c871accd2..8fd53b3f4 100644 --- a/proto/neutron/interchaintxs/v1/tx.proto +++ b/proto/neutron/interchaintxs/v1/tx.proto @@ -4,6 +4,7 @@ package neutron.interchaintxs.v1; option go_package = "github.com/neutron-org/neutron/x/interchaintxs/types"; import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; import "gogoproto/gogo.proto"; import "google/api/http.proto"; import "google/api/annotations.proto"; @@ -26,6 +27,10 @@ message MsgRegisterInterchainAccount { string connection_id = 2 [ (gogoproto.moretags) = "yaml:\"connection_id\"" ]; string interchain_account_id = 3 [ (gogoproto.moretags) = "yaml:\"interchain_account_id\"" ]; + repeated cosmos.base.v1beta1.Coin register_fee = 4 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; } // MsgRegisterInterchainAccountResponse is the response type for diff --git a/testutil/interchaintxs/keeper/interchaintxs.go b/testutil/interchaintxs/keeper/interchaintxs.go index 55c9b43a9..51d23413f 100644 --- a/testutil/interchaintxs/keeper/interchaintxs.go +++ b/testutil/interchaintxs/keeper/interchaintxs.go @@ -17,7 +17,14 @@ import ( "github.com/neutron-org/neutron/x/interchaintxs/types" ) -func InterchainTxsKeeper(t testing.TB, managerKeeper types.ContractManagerKeeper, refunderKeeper types.FeeRefunderKeeper, icaControllerKeeper types.ICAControllerKeeper, channelKeeper types.ChannelKeeper) (*keeper.Keeper, sdk.Context, *storetypes.KVStoreKey) { +func InterchainTxsKeeper( + t testing.TB, + managerKeeper types.WasmKeeper, + refunderKeeper types.FeeRefunderKeeper, + icaControllerKeeper types.ICAControllerKeeper, + channelKeeper types.ChannelKeeper, + bankKeeper types.BankKeeper, + feeburnerKeeper types.FeeBurnerKeeper) (*keeper.Keeper, sdk.Context) { storeKey := sdk.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) @@ -38,6 +45,8 @@ func InterchainTxsKeeper(t testing.TB, managerKeeper types.ContractManagerKeeper icaControllerKeeper, managerKeeper, refunderKeeper, + bankKeeper, + feeburnerKeeper, ) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) @@ -46,5 +55,5 @@ func InterchainTxsKeeper(t testing.TB, managerKeeper types.ContractManagerKeeper err := k.SetParams(ctx, types.DefaultParams()) require.NoError(t, err) - return k, ctx, storeKey + return k, ctx } diff --git a/testutil/interchaintxs/keeper/sudo_middleware.go b/testutil/interchaintxs/keeper/sudo_middleware.go new file mode 100644 index 000000000..5d586f342 --- /dev/null +++ b/testutil/interchaintxs/keeper/sudo_middleware.go @@ -0,0 +1,27 @@ +package keeper + +import ( + tmdb "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/neutron-org/neutron/x/contractmanager" + "github.com/neutron-org/neutron/x/contractmanager/types" + "github.com/stretchr/testify/require" + "testing" +) + +func NewSudoLimitWrapper(t testing.TB, cmKeeper types.ContractManagerKeeper, wasmKeeper types.WasmKeeper) (types.WasmKeeper, sdk.Context, *storetypes.KVStoreKey) { + storeKey := sdk.NewKVStoreKey(types.StoreKey) + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) + require.NoError(t, stateStore.LoadLatestVersion()) + + limitWrapper := contractmanager.NewSudoLimitWrapper(cmKeeper, wasmKeeper) + ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + + return limitWrapper, ctx, storeKey +} diff --git a/testutil/mocks/contractmanager/types/expected_keepers.go b/testutil/mocks/contractmanager/types/expected_keepers.go index 463ef15c4..d27f64e5f 100644 --- a/testutil/mocks/contractmanager/types/expected_keepers.go +++ b/testutil/mocks/contractmanager/types/expected_keepers.go @@ -9,6 +9,7 @@ import ( types "github.com/cosmos/cosmos-sdk/types" gomock "github.com/golang/mock/gomock" + types0 "github.com/neutron-org/neutron/x/contractmanager/types" ) // MockWasmKeeper is a mock of WasmKeeper interface. @@ -62,3 +63,52 @@ func (mr *MockWasmKeeperMockRecorder) Sudo(ctx, contractAddress, msg interface{} mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sudo", reflect.TypeOf((*MockWasmKeeper)(nil).Sudo), ctx, contractAddress, msg) } + +// MockContractManagerKeeper is a mock of ContractManagerKeeper interface. +type MockContractManagerKeeper struct { + ctrl *gomock.Controller + recorder *MockContractManagerKeeperMockRecorder +} + +// MockContractManagerKeeperMockRecorder is the mock recorder for MockContractManagerKeeper. +type MockContractManagerKeeperMockRecorder struct { + mock *MockContractManagerKeeper +} + +// NewMockContractManagerKeeper creates a new mock instance. +func NewMockContractManagerKeeper(ctrl *gomock.Controller) *MockContractManagerKeeper { + mock := &MockContractManagerKeeper{ctrl: ctrl} + mock.recorder = &MockContractManagerKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockContractManagerKeeper) EXPECT() *MockContractManagerKeeperMockRecorder { + return m.recorder +} + +// AddContractFailure mocks base method. +func (m *MockContractManagerKeeper) AddContractFailure(ctx types.Context, address string, sudoPayload []byte) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddContractFailure", ctx, address, sudoPayload) +} + +// AddContractFailure indicates an expected call of AddContractFailure. +func (mr *MockContractManagerKeeperMockRecorder) AddContractFailure(ctx, address, sudoPayload interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddContractFailure", reflect.TypeOf((*MockContractManagerKeeper)(nil).AddContractFailure), ctx, address, sudoPayload) +} + +// GetParams mocks base method. +func (m *MockContractManagerKeeper) GetParams(ctx types.Context) types0.Params { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetParams", ctx) + ret0, _ := ret[0].(types0.Params) + return ret0 +} + +// GetParams indicates an expected call of GetParams. +func (mr *MockContractManagerKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockContractManagerKeeper)(nil).GetParams), ctx) +} diff --git a/testutil/mocks/interchainqueries/types/expected_keepers.go b/testutil/mocks/interchainqueries/types/expected_keepers.go index 435a54b05..029ca0135 100644 --- a/testutil/mocks/interchainqueries/types/expected_keepers.go +++ b/testutil/mocks/interchainqueries/types/expected_keepers.go @@ -10,7 +10,6 @@ import ( types "github.com/cosmos/cosmos-sdk/types" types0 "github.com/cosmos/cosmos-sdk/x/auth/types" types1 "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - types2 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" gomock "github.com/golang/mock/gomock" ) @@ -153,21 +152,6 @@ func (mr *MockContractManagerKeeperMockRecorder) HasContractInfo(ctx, contractAd return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasContractInfo", reflect.TypeOf((*MockContractManagerKeeper)(nil).HasContractInfo), ctx, contractAddress) } -// SudoError mocks base method. -func (m *MockContractManagerKeeper) SudoError(ctx types.Context, senderAddress types.AccAddress, request types2.Packet, details string) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoError", ctx, senderAddress, request, details) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SudoError indicates an expected call of SudoError. -func (mr *MockContractManagerKeeperMockRecorder) SudoError(ctx, senderAddress, request, details interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoError", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoError), ctx, senderAddress, request, details) -} - // SudoKVQueryResult mocks base method. func (m *MockContractManagerKeeper) SudoKVQueryResult(ctx types.Context, contractAddress types.AccAddress, queryID uint64) ([]byte, error) { m.ctrl.T.Helper() @@ -183,36 +167,6 @@ func (mr *MockContractManagerKeeperMockRecorder) SudoKVQueryResult(ctx, contract return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoKVQueryResult", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoKVQueryResult), ctx, contractAddress, queryID) } -// SudoResponse mocks base method. -func (m *MockContractManagerKeeper) SudoResponse(ctx types.Context, senderAddress types.AccAddress, request types2.Packet, msg []byte) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, msg) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SudoResponse indicates an expected call of SudoResponse. -func (mr *MockContractManagerKeeperMockRecorder) SudoResponse(ctx, senderAddress, request, msg interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoResponse", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoResponse), ctx, senderAddress, request, msg) -} - -// SudoTimeout mocks base method. -func (m *MockContractManagerKeeper) SudoTimeout(ctx types.Context, senderAddress types.AccAddress, request types2.Packet) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoTimeout", ctx, senderAddress, request) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SudoTimeout indicates an expected call of SudoTimeout. -func (mr *MockContractManagerKeeperMockRecorder) SudoTimeout(ctx, senderAddress, request interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoTimeout", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoTimeout), ctx, senderAddress, request) -} - // SudoTxQueryResult mocks base method. func (m *MockContractManagerKeeper) SudoTxQueryResult(ctx types.Context, contractAddress types.AccAddress, queryID uint64, height types1.Height, data []byte) ([]byte, error) { m.ctrl.T.Helper() diff --git a/testutil/mocks/interchaintxs/types/expected_keepers.go b/testutil/mocks/interchaintxs/types/expected_keepers.go index e3d9c948a..97633afb9 100644 --- a/testutil/mocks/interchaintxs/types/expected_keepers.go +++ b/testutil/mocks/interchaintxs/types/expected_keepers.go @@ -14,7 +14,7 @@ import ( types3 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" exported "github.com/cosmos/ibc-go/v7/modules/core/exported" gomock "github.com/golang/mock/gomock" - types4 "github.com/neutron-org/neutron/x/contractmanager/types" + types4 "github.com/neutron-org/neutron/x/feeburner/types" types5 "github.com/neutron-org/neutron/x/feerefunder/types" ) @@ -78,6 +78,20 @@ func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { return m.recorder } +// SendCoins mocks base method. +func (m *MockBankKeeper) SendCoins(ctx types.Context, fromAddr, toAddr types.AccAddress, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoins", ctx, fromAddr, toAddr, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoins indicates an expected call of SendCoins. +func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoins", reflect.TypeOf((*MockBankKeeper)(nil).SendCoins), ctx, fromAddr, toAddr, amt) +} + // SpendableCoins mocks base method. func (m *MockBankKeeper) SpendableCoins(ctx types.Context, addr types.AccAddress) types.Coins { m.ctrl.T.Helper() @@ -92,57 +106,31 @@ func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr) } -// MockContractManagerKeeper is a mock of ContractManagerKeeper interface. -type MockContractManagerKeeper struct { +// MockWasmKeeper is a mock of WasmKeeper interface. +type MockWasmKeeper struct { ctrl *gomock.Controller - recorder *MockContractManagerKeeperMockRecorder + recorder *MockWasmKeeperMockRecorder } -// MockContractManagerKeeperMockRecorder is the mock recorder for MockContractManagerKeeper. -type MockContractManagerKeeperMockRecorder struct { - mock *MockContractManagerKeeper +// MockWasmKeeperMockRecorder is the mock recorder for MockWasmKeeper. +type MockWasmKeeperMockRecorder struct { + mock *MockWasmKeeper } -// NewMockContractManagerKeeper creates a new mock instance. -func NewMockContractManagerKeeper(ctrl *gomock.Controller) *MockContractManagerKeeper { - mock := &MockContractManagerKeeper{ctrl: ctrl} - mock.recorder = &MockContractManagerKeeperMockRecorder{mock} +// NewMockWasmKeeper creates a new mock instance. +func NewMockWasmKeeper(ctrl *gomock.Controller) *MockWasmKeeper { + mock := &MockWasmKeeper{ctrl: ctrl} + mock.recorder = &MockWasmKeeperMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockContractManagerKeeper) EXPECT() *MockContractManagerKeeperMockRecorder { +func (m *MockWasmKeeper) EXPECT() *MockWasmKeeperMockRecorder { return m.recorder } -// AddContractFailure mocks base method. -func (m *MockContractManagerKeeper) AddContractFailure(ctx types.Context, packet *types3.Packet, address, ackType string, ack *types3.Acknowledgement) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddContractFailure", ctx, packet, address, ackType, ack) -} - -// AddContractFailure indicates an expected call of AddContractFailure. -func (mr *MockContractManagerKeeperMockRecorder) AddContractFailure(ctx, packet, address, ackType, ack interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddContractFailure", reflect.TypeOf((*MockContractManagerKeeper)(nil).AddContractFailure), ctx, packet, address, ackType, ack) -} - -// GetParams mocks base method. -func (m *MockContractManagerKeeper) GetParams(ctx types.Context) types4.Params { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetParams", ctx) - ret0, _ := ret[0].(types4.Params) - return ret0 -} - -// GetParams indicates an expected call of GetParams. -func (mr *MockContractManagerKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockContractManagerKeeper)(nil).GetParams), ctx) -} - // HasContractInfo mocks base method. -func (m *MockContractManagerKeeper) HasContractInfo(ctx types.Context, contractAddress types.AccAddress) bool { +func (m *MockWasmKeeper) HasContractInfo(ctx types.Context, contractAddress types.AccAddress) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasContractInfo", ctx, contractAddress) ret0, _ := ret[0].(bool) @@ -150,69 +138,24 @@ func (m *MockContractManagerKeeper) HasContractInfo(ctx types.Context, contractA } // HasContractInfo indicates an expected call of HasContractInfo. -func (mr *MockContractManagerKeeperMockRecorder) HasContractInfo(ctx, contractAddress interface{}) *gomock.Call { +func (mr *MockWasmKeeperMockRecorder) HasContractInfo(ctx, contractAddress interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasContractInfo", reflect.TypeOf((*MockContractManagerKeeper)(nil).HasContractInfo), ctx, contractAddress) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasContractInfo", reflect.TypeOf((*MockWasmKeeper)(nil).HasContractInfo), ctx, contractAddress) } -// SudoError mocks base method. -func (m *MockContractManagerKeeper) SudoError(ctx types.Context, senderAddress types.AccAddress, request types3.Packet, details string) ([]byte, error) { +// Sudo mocks base method. +func (m *MockWasmKeeper) Sudo(ctx types.Context, contractAddress types.AccAddress, msg []byte) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoError", ctx, senderAddress, request, details) + ret := m.ctrl.Call(m, "Sudo", ctx, contractAddress, msg) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } -// SudoError indicates an expected call of SudoError. -func (mr *MockContractManagerKeeperMockRecorder) SudoError(ctx, senderAddress, request, details interface{}) *gomock.Call { +// Sudo indicates an expected call of Sudo. +func (mr *MockWasmKeeperMockRecorder) Sudo(ctx, contractAddress, msg interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoError", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoError), ctx, senderAddress, request, details) -} - -// SudoOnChanOpenAck mocks base method. -func (m *MockContractManagerKeeper) SudoOnChanOpenAck(ctx types.Context, contractAddress types.AccAddress, details types4.OpenAckDetails) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoOnChanOpenAck", ctx, contractAddress, details) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SudoOnChanOpenAck indicates an expected call of SudoOnChanOpenAck. -func (mr *MockContractManagerKeeperMockRecorder) SudoOnChanOpenAck(ctx, contractAddress, details interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoOnChanOpenAck", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoOnChanOpenAck), ctx, contractAddress, details) -} - -// SudoResponse mocks base method. -func (m *MockContractManagerKeeper) SudoResponse(ctx types.Context, senderAddress types.AccAddress, request types3.Packet, msg []byte) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, msg) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SudoResponse indicates an expected call of SudoResponse. -func (mr *MockContractManagerKeeperMockRecorder) SudoResponse(ctx, senderAddress, request, msg interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoResponse", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoResponse), ctx, senderAddress, request, msg) -} - -// SudoTimeout mocks base method. -func (m *MockContractManagerKeeper) SudoTimeout(ctx types.Context, senderAddress types.AccAddress, request types3.Packet) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoTimeout", ctx, senderAddress, request) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SudoTimeout indicates an expected call of SudoTimeout. -func (mr *MockContractManagerKeeperMockRecorder) SudoTimeout(ctx, senderAddress, request interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoTimeout", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoTimeout), ctx, senderAddress, request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sudo", reflect.TypeOf((*MockWasmKeeper)(nil).Sudo), ctx, contractAddress, msg) } // MockICAControllerKeeper is a mock of ICAControllerKeeper interface. @@ -425,3 +368,40 @@ func (mr *MockChannelKeeperMockRecorder) GetNextSequenceSend(ctx, portID, channe mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNextSequenceSend", reflect.TypeOf((*MockChannelKeeper)(nil).GetNextSequenceSend), ctx, portID, channelID) } + +// MockFeeBurnerKeeper is a mock of FeeBurnerKeeper interface. +type MockFeeBurnerKeeper struct { + ctrl *gomock.Controller + recorder *MockFeeBurnerKeeperMockRecorder +} + +// MockFeeBurnerKeeperMockRecorder is the mock recorder for MockFeeBurnerKeeper. +type MockFeeBurnerKeeperMockRecorder struct { + mock *MockFeeBurnerKeeper +} + +// NewMockFeeBurnerKeeper creates a new mock instance. +func NewMockFeeBurnerKeeper(ctrl *gomock.Controller) *MockFeeBurnerKeeper { + mock := &MockFeeBurnerKeeper{ctrl: ctrl} + mock.recorder = &MockFeeBurnerKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFeeBurnerKeeper) EXPECT() *MockFeeBurnerKeeperMockRecorder { + return m.recorder +} + +// GetParams mocks base method. +func (m *MockFeeBurnerKeeper) GetParams(ctx types.Context) types4.Params { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetParams", ctx) + ret0, _ := ret[0].(types4.Params) + return ret0 +} + +// GetParams indicates an expected call of GetParams. +func (mr *MockFeeBurnerKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockFeeBurnerKeeper)(nil).GetParams), ctx) +} diff --git a/testutil/mocks/transfer/types/expected_keepers.go b/testutil/mocks/transfer/types/expected_keepers.go index f5c4d2ab1..3123b6eb5 100644 --- a/testutil/mocks/transfer/types/expected_keepers.go +++ b/testutil/mocks/transfer/types/expected_keepers.go @@ -11,61 +11,34 @@ import ( types0 "github.com/cosmos/cosmos-sdk/x/auth/types" types1 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" gomock "github.com/golang/mock/gomock" - types2 "github.com/neutron-org/neutron/x/contractmanager/types" - types3 "github.com/neutron-org/neutron/x/feerefunder/types" + types2 "github.com/neutron-org/neutron/x/feerefunder/types" ) -// MockContractManagerKeeper is a mock of ContractManagerKeeper interface. -type MockContractManagerKeeper struct { +// MockWasmKeeper is a mock of WasmKeeper interface. +type MockWasmKeeper struct { ctrl *gomock.Controller - recorder *MockContractManagerKeeperMockRecorder + recorder *MockWasmKeeperMockRecorder } -// MockContractManagerKeeperMockRecorder is the mock recorder for MockContractManagerKeeper. -type MockContractManagerKeeperMockRecorder struct { - mock *MockContractManagerKeeper +// MockWasmKeeperMockRecorder is the mock recorder for MockWasmKeeper. +type MockWasmKeeperMockRecorder struct { + mock *MockWasmKeeper } -// NewMockContractManagerKeeper creates a new mock instance. -func NewMockContractManagerKeeper(ctrl *gomock.Controller) *MockContractManagerKeeper { - mock := &MockContractManagerKeeper{ctrl: ctrl} - mock.recorder = &MockContractManagerKeeperMockRecorder{mock} +// NewMockWasmKeeper creates a new mock instance. +func NewMockWasmKeeper(ctrl *gomock.Controller) *MockWasmKeeper { + mock := &MockWasmKeeper{ctrl: ctrl} + mock.recorder = &MockWasmKeeperMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockContractManagerKeeper) EXPECT() *MockContractManagerKeeperMockRecorder { +func (m *MockWasmKeeper) EXPECT() *MockWasmKeeperMockRecorder { return m.recorder } -// AddContractFailure mocks base method. -func (m *MockContractManagerKeeper) AddContractFailure(ctx types.Context, packet *types1.Packet, address, ackType string, ack *types1.Acknowledgement) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddContractFailure", ctx, packet, address, ackType, ack) -} - -// AddContractFailure indicates an expected call of AddContractFailure. -func (mr *MockContractManagerKeeperMockRecorder) AddContractFailure(ctx, packet, address, ackType, ack interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddContractFailure", reflect.TypeOf((*MockContractManagerKeeper)(nil).AddContractFailure), ctx, packet, address, ackType, ack) -} - -// GetParams mocks base method. -func (m *MockContractManagerKeeper) GetParams(ctx types.Context) types2.Params { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetParams", ctx) - ret0, _ := ret[0].(types2.Params) - return ret0 -} - -// GetParams indicates an expected call of GetParams. -func (mr *MockContractManagerKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockContractManagerKeeper)(nil).GetParams), ctx) -} - // HasContractInfo mocks base method. -func (m *MockContractManagerKeeper) HasContractInfo(ctx types.Context, contractAddress types.AccAddress) bool { +func (m *MockWasmKeeper) HasContractInfo(ctx types.Context, contractAddress types.AccAddress) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasContractInfo", ctx, contractAddress) ret0, _ := ret[0].(bool) @@ -73,54 +46,24 @@ func (m *MockContractManagerKeeper) HasContractInfo(ctx types.Context, contractA } // HasContractInfo indicates an expected call of HasContractInfo. -func (mr *MockContractManagerKeeperMockRecorder) HasContractInfo(ctx, contractAddress interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasContractInfo", reflect.TypeOf((*MockContractManagerKeeper)(nil).HasContractInfo), ctx, contractAddress) -} - -// SudoError mocks base method. -func (m *MockContractManagerKeeper) SudoError(ctx types.Context, senderAddress types.AccAddress, request types1.Packet, details string) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoError", ctx, senderAddress, request, details) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SudoError indicates an expected call of SudoError. -func (mr *MockContractManagerKeeperMockRecorder) SudoError(ctx, senderAddress, request, details interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoError", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoError), ctx, senderAddress, request, details) -} - -// SudoResponse mocks base method. -func (m *MockContractManagerKeeper) SudoResponse(ctx types.Context, senderAddress types.AccAddress, request types1.Packet, msg []byte) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, msg) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SudoResponse indicates an expected call of SudoResponse. -func (mr *MockContractManagerKeeperMockRecorder) SudoResponse(ctx, senderAddress, request, msg interface{}) *gomock.Call { +func (mr *MockWasmKeeperMockRecorder) HasContractInfo(ctx, contractAddress interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoResponse", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoResponse), ctx, senderAddress, request, msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasContractInfo", reflect.TypeOf((*MockWasmKeeper)(nil).HasContractInfo), ctx, contractAddress) } -// SudoTimeout mocks base method. -func (m *MockContractManagerKeeper) SudoTimeout(ctx types.Context, senderAddress types.AccAddress, request types1.Packet) ([]byte, error) { +// Sudo mocks base method. +func (m *MockWasmKeeper) Sudo(ctx types.Context, contractAddress types.AccAddress, msg []byte) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoTimeout", ctx, senderAddress, request) + ret := m.ctrl.Call(m, "Sudo", ctx, contractAddress, msg) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } -// SudoTimeout indicates an expected call of SudoTimeout. -func (mr *MockContractManagerKeeperMockRecorder) SudoTimeout(ctx, senderAddress, request interface{}) *gomock.Call { +// Sudo indicates an expected call of Sudo. +func (mr *MockWasmKeeperMockRecorder) Sudo(ctx, contractAddress, msg interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoTimeout", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoTimeout), ctx, senderAddress, request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sudo", reflect.TypeOf((*MockWasmKeeper)(nil).Sudo), ctx, contractAddress, msg) } // MockFeeRefunderKeeper is a mock of FeeRefunderKeeper interface. @@ -147,7 +90,7 @@ func (m *MockFeeRefunderKeeper) EXPECT() *MockFeeRefunderKeeperMockRecorder { } // DistributeAcknowledgementFee mocks base method. -func (m *MockFeeRefunderKeeper) DistributeAcknowledgementFee(ctx types.Context, receiver types.AccAddress, packetID types3.PacketID) { +func (m *MockFeeRefunderKeeper) DistributeAcknowledgementFee(ctx types.Context, receiver types.AccAddress, packetID types2.PacketID) { m.ctrl.T.Helper() m.ctrl.Call(m, "DistributeAcknowledgementFee", ctx, receiver, packetID) } @@ -159,7 +102,7 @@ func (mr *MockFeeRefunderKeeperMockRecorder) DistributeAcknowledgementFee(ctx, r } // DistributeTimeoutFee mocks base method. -func (m *MockFeeRefunderKeeper) DistributeTimeoutFee(ctx types.Context, receiver types.AccAddress, packetID types3.PacketID) { +func (m *MockFeeRefunderKeeper) DistributeTimeoutFee(ctx types.Context, receiver types.AccAddress, packetID types2.PacketID) { m.ctrl.T.Helper() m.ctrl.Call(m, "DistributeTimeoutFee", ctx, receiver, packetID) } @@ -171,7 +114,7 @@ func (mr *MockFeeRefunderKeeperMockRecorder) DistributeTimeoutFee(ctx, receiver, } // LockFees mocks base method. -func (m *MockFeeRefunderKeeper) LockFees(ctx types.Context, payer types.AccAddress, packetID types3.PacketID, fee types3.Fee) error { +func (m *MockFeeRefunderKeeper) LockFees(ctx types.Context, payer types.AccAddress, packetID types2.PacketID, fee types2.Fee) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockFees", ctx, payer, packetID, fee) ret0, _ := ret[0].(error) diff --git a/testutil/transfer/keeper/keeper.go b/testutil/transfer/keeper/keeper.go index 8f35aa8a6..532701629 100644 --- a/testutil/transfer/keeper/keeper.go +++ b/testutil/transfer/keeper/keeper.go @@ -22,7 +22,7 @@ import ( "github.com/neutron-org/neutron/x/transfer/types" ) -func TransferKeeper(t testing.TB, managerKeeper types.ContractManagerKeeper, refunderKeeper types.FeeRefunderKeeper, channelKeeper types.ChannelKeeper, authKeeper types.AccountKeeper) (*keeper.KeeperTransferWrapper, sdk.Context, *storetypes.KVStoreKey) { +func TransferKeeper(t testing.TB, managerKeeper types.WasmKeeper, refunderKeeper types.FeeRefunderKeeper, channelKeeper types.ChannelKeeper, authKeeper types.AccountKeeper) (*keeper.KeeperTransferWrapper, sdk.Context, *storetypes.KVStoreKey) { storeKey := sdk.NewKVStoreKey(transfertypes.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey("mem_" + transfertypes.StoreKey) diff --git a/wasmbinding/bindings/msg.go b/wasmbinding/bindings/msg.go index 13793b11b..b2fd53f51 100644 --- a/wasmbinding/bindings/msg.go +++ b/wasmbinding/bindings/msg.go @@ -4,6 +4,7 @@ package bindings import ( "cosmossdk.io/math" cosmostypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" paramChange "github.com/cosmos/cosmos-sdk/x/params/types/proposal" feetypes "github.com/neutron-org/neutron/x/feerefunder/types" @@ -72,8 +73,9 @@ type SubmitTxResponse struct { // RegisterInterchainAccount creates account on remote chain. type RegisterInterchainAccount struct { - ConnectionId string `json:"connection_id"` - InterchainAccountId string `json:"interchain_account_id"` + ConnectionId string `json:"connection_id"` + InterchainAccountId string `json:"interchain_account_id"` + RegisterFee sdk.Coins `json:"register_fee"` } // RegisterInterchainAccountResponse holds response for RegisterInterchainAccount. diff --git a/wasmbinding/message_plugin.go b/wasmbinding/message_plugin.go index 2768ba7a3..383ac6d48 100644 --- a/wasmbinding/message_plugin.go +++ b/wasmbinding/message_plugin.go @@ -691,6 +691,7 @@ func (m *CustomMessenger) performRegisterInterchainAccount(ctx sdk.Context, cont FromAddress: contractAddr.String(), ConnectionId: reg.ConnectionId, InterchainAccountId: reg.InterchainAccountId, + RegisterFee: reg.RegisterFee, } if err := msg.ValidateBasic(); err != nil { return nil, errors.Wrap(err, "failed to validate incoming RegisterInterchainAccount message") diff --git a/wasmbinding/test/custom_message_test.go b/wasmbinding/test/custom_message_test.go index d802dbe44..5346823d7 100644 --- a/wasmbinding/test/custom_message_test.go +++ b/wasmbinding/test/custom_message_test.go @@ -3,10 +3,10 @@ package test import ( "encoding/json" "fmt" + keeper2 "github.com/neutron-org/neutron/x/contractmanager/keeper" + feeburnertypes "github.com/neutron-org/neutron/x/feeburner/types" "testing" - contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" - ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" "github.com/stretchr/testify/suite" @@ -77,15 +77,27 @@ func (suite *CustomMessengerTestSuite) TestRegisterInterchainAccount() { suite.contractAddress = suite.InstantiateReflectContract(suite.ctx, suite.contractOwner, codeID) suite.Require().NotEmpty(suite.contractAddress) + err := suite.neutron.FeeBurnerKeeper.SetParams(suite.ctx, feeburnertypes.Params{ + NeutronDenom: "untrn", + TreasuryAddress: "neutron13jrwrtsyjjuynlug65r76r2zvfw5xjcq6532h2", + }) + suite.Require().NoError(err) + // Craft RegisterInterchainAccount message msg, err := json.Marshal(bindings.NeutronMsg{ RegisterInterchainAccount: &bindings.RegisterInterchainAccount{ ConnectionId: suite.Path.EndpointA.ConnectionID, InterchainAccountId: testutil.TestInterchainID, + RegisterFee: sdk.NewCoins(sdk.NewCoin(params.DefaultDenom, sdk.NewInt(1000))), }, }) suite.NoError(err) + bankKeeper := suite.neutron.BankKeeper + senderAddress := suite.ChainA.SenderAccounts[0].SenderAccount.GetAddress() + err = bankKeeper.SendCoins(suite.ctx, senderAddress, suite.contractAddress, sdk.NewCoins(sdk.NewCoin(params.DefaultDenom, sdk.NewInt(1000)))) + suite.NoError(err) + // Dispatch RegisterInterchainAccount message events, data, err := suite.messenger.DispatchMsg(suite.ctx, suite.contractAddress, suite.Path.EndpointA.ChannelConfig.PortID, types.CosmosMsg{ Custom: msg, @@ -609,8 +621,10 @@ func (suite *CustomMessengerTestSuite) TestResubmitFailureAck() { ack := ibcchanneltypes.Acknowledgement{ Response: &ibcchanneltypes.Acknowledgement_Result{Result: []byte("Result")}, } + payload, err := keeper2.PrepareSudoCallbackMessage(packet, &ack) + require.NoError(suite.T(), err) failureID := suite.messenger.ContractmanagerKeeper.GetNextFailureIDKey(suite.ctx, suite.contractAddress.String()) - suite.messenger.ContractmanagerKeeper.AddContractFailure(suite.ctx, &packet, suite.contractAddress.String(), contractmanagertypes.Ack, &ack) + suite.messenger.ContractmanagerKeeper.AddContractFailure(suite.ctx, suite.contractAddress.String(), payload) // Craft message msg, err := json.Marshal(bindings.NeutronMsg{ @@ -639,11 +653,10 @@ func (suite *CustomMessengerTestSuite) TestResubmitFailureTimeout() { // Add failure packet := ibcchanneltypes.Packet{} - ack := ibcchanneltypes.Acknowledgement{ - Response: &ibcchanneltypes.Acknowledgement_Error{Error: "Error"}, - } + payload, err := keeper2.PrepareSudoCallbackMessage(packet, nil) + require.NoError(suite.T(), err) failureID := suite.messenger.ContractmanagerKeeper.GetNextFailureIDKey(suite.ctx, suite.contractAddress.String()) - suite.messenger.ContractmanagerKeeper.AddContractFailure(suite.ctx, &packet, suite.contractAddress.String(), "timeout", &ack) + suite.messenger.ContractmanagerKeeper.AddContractFailure(suite.ctx, suite.contractAddress.String(), payload) // Craft message msg, err := json.Marshal(bindings.NeutronMsg{ @@ -673,10 +686,12 @@ func (suite *CustomMessengerTestSuite) TestResubmitFailureFromDifferentContract( // Add failure packet := ibcchanneltypes.Packet{} ack := ibcchanneltypes.Acknowledgement{ - Response: &ibcchanneltypes.Acknowledgement_Error{Error: "Error"}, + Response: &ibcchanneltypes.Acknowledgement_Error{Error: "ErrorSudoPayload"}, } failureID := suite.messenger.ContractmanagerKeeper.GetNextFailureIDKey(suite.ctx, testutil.TestOwnerAddress) - suite.messenger.ContractmanagerKeeper.AddContractFailure(suite.ctx, &packet, testutil.TestOwnerAddress, contractmanagertypes.Ack, &ack) + payload, err := keeper2.PrepareSudoCallbackMessage(packet, &ack) + require.NoError(suite.T(), err) + suite.messenger.ContractmanagerKeeper.AddContractFailure(suite.ctx, testutil.TestOwnerAddress, payload) // Craft message msg, err := json.Marshal(bindings.NeutronMsg{ diff --git a/x/contractmanager/client/cli/query_failure_test.go b/x/contractmanager/client/cli/query_failure_test.go index f76d69a44..d590d39e3 100644 --- a/x/contractmanager/client/cli/query_failure_test.go +++ b/x/contractmanager/client/cli/query_failure_test.go @@ -6,8 +6,6 @@ import ( "strconv" "testing" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - "github.com/neutron-org/neutron/app" tmcli "github.com/cometbft/cometbft/libs/cli" @@ -40,8 +38,8 @@ func networkWithFailureObjects(t *testing.T, n int) (*network.Network, []types.F require.NoError(t, err) acc := sdktypes.AccAddress(pub.Address()) failure := types.Failure{ - Address: acc.String(), - Packet: &channeltypes.Packet{}, + Address: acc.String(), + SudoPayload: []byte("&channeltypes.Packet{}"), } nullify.Fill(&failure) state.FailuresList = append(state.FailuresList, failure) diff --git a/x/contractmanager/genesis.go b/x/contractmanager/genesis.go index 35604dfe4..fd5751c6b 100644 --- a/x/contractmanager/genesis.go +++ b/x/contractmanager/genesis.go @@ -11,7 +11,7 @@ import ( func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { // Set all the failure for _, elem := range genState.FailuresList { - k.AddContractFailure(ctx, elem.Packet, elem.Address, elem.AckType, elem.Ack) + k.AddContractFailure(ctx, elem.Address, elem.SudoPayload) } // this line is used by starport scaffolding # genesis/module/init err := k.SetParams(ctx, genState.Params) diff --git a/x/contractmanager/genesis_test.go b/x/contractmanager/genesis_test.go index feb3ff00a..ddfa24682 100644 --- a/x/contractmanager/genesis_test.go +++ b/x/contractmanager/genesis_test.go @@ -1,6 +1,7 @@ package contractmanager_test import ( + "github.com/neutron-org/neutron/x/contractmanager/keeper" "testing" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" @@ -14,25 +15,33 @@ import ( ) func TestGenesis(t *testing.T) { + payload1, err := keeper.PrepareSudoCallbackMessage( + channeltypes.Packet{ + Sequence: 1, + }, + &channeltypes.Acknowledgement{ + Response: &channeltypes.Acknowledgement_Result{ + Result: []byte("Result"), + }, + }) + require.NoError(t, err) + payload2, err := keeper.PrepareSudoCallbackMessage( + channeltypes.Packet{ + Sequence: 2, + }, nil) genesisState := types.GenesisState{ Params: types.DefaultParams(), FailuresList: []types.Failure{ { - Address: "address1", - Id: 1, - AckType: types.Ack, - Packet: &channeltypes.Packet{ - Sequence: 1, - }, + Address: "address1", + Id: 1, + SudoPayload: payload1, }, { - Address: "address1", - Id: 2, - AckType: "timeout", - Packet: &channeltypes.Packet{ - Sequence: 2, - }, + Address: "address1", + Id: 2, + SudoPayload: payload2, }, }, // this line is used by starport scaffolding # genesis/test/state diff --git a/x/contractmanager/ibc_middleware.go b/x/contractmanager/ibc_middleware.go new file mode 100644 index 000000000..739de0860 --- /dev/null +++ b/x/contractmanager/ibc_middleware.go @@ -0,0 +1,72 @@ +package contractmanager + +import ( + "cosmossdk.io/errors" + "fmt" + "github.com/cometbft/cometbft/libs/log" + sdk "github.com/cosmos/cosmos-sdk/types" + contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" +) + +type SudoLimitWrapper struct { + contractManager contractmanagertypes.ContractManagerKeeper + contractmanagertypes.WasmKeeper +} + +// NewSudoLimitWrapper suppresses an error from a Sudo contract handler and saves it to a store +func NewSudoLimitWrapper(contractManager contractmanagertypes.ContractManagerKeeper, sudoKeeper contractmanagertypes.WasmKeeper) contractmanagertypes.WasmKeeper { + return SudoLimitWrapper{ + contractManager, + sudoKeeper, + } +} + +// Sudo calls underlying Sudo handlers with a limited amount of gas +// in case of `out of gas` panic it converts the panic into an error and stops `out of gas` panic propagation +// if error happens during the Sudo call, we store the data that raised the error, and return the error +func (k SudoLimitWrapper) Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) (resp []byte, err error) { + cacheCtx, writeFn := createCachedContext(ctx, k.contractManager.GetParams(ctx).SudoCallGasLimit) + func() { + defer outOfGasRecovery(cacheCtx.GasMeter(), &err) + // Actually we have only one kind of error returned from acknowledgement + // maybe later we'll retrieve actual errors from events + resp, err = k.WasmKeeper.Sudo(cacheCtx, contractAddress, msg) + + }() + if err != nil { + // the contract either returned an error or panicked with `out of gas` + k.contractManager.AddContractFailure(ctx, contractAddress.String(), msg) + } else { + writeFn() + } + + ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") + return +} + +func (k SudoLimitWrapper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", contractmanagertypes.ModuleName)) +} + +// outOfGasRecovery converts `out of gas` panic into an error +// leaving unprocessed any other kinds of panics +func outOfGasRecovery( + gasMeter sdk.GasMeter, + err *error, +) { + if r := recover(); r != nil { + _, ok := r.(sdk.ErrorOutOfGas) + if !ok || !gasMeter.IsOutOfGas() { + panic(r) + } + *err = errors.Wrapf(errors.ErrPanic, "%v", r) + } +} + +// createCachedContext creates a cached context with a limited gas meter. +func createCachedContext(ctx sdk.Context, gasLimit uint64) (sdk.Context, func()) { + cacheCtx, writeFn := ctx.CacheContext() + gasMeter := sdk.NewGasMeter(gasLimit) + cacheCtx = cacheCtx.WithGasMeter(gasMeter) + return cacheCtx, writeFn +} diff --git a/x/contractmanager/ibc_middleware_test.go b/x/contractmanager/ibc_middleware_test.go new file mode 100644 index 000000000..0815be992 --- /dev/null +++ b/x/contractmanager/ibc_middleware_test.go @@ -0,0 +1,72 @@ +package contractmanager_test + +import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" + test_keeper "github.com/neutron-org/neutron/testutil/interchaintxs/keeper" + mock_types "github.com/neutron-org/neutron/testutil/mocks/contractmanager/types" + "github.com/neutron-org/neutron/x/contractmanager/types" + "github.com/stretchr/testify/require" + "testing" +) + +var ( + ShouldNotBeWrittenKey = []byte("shouldnotkey") + ShouldNotBeWritten = []byte("should not be written") + ShouldBeWritten = []byte("should be written") + TestOwnerAddress = "neutron17dtl0mjt3t77kpuhg2edqzjpszulwhgzcdvagh" +) + +func ShouldBeWrittenKey(suffix string) []byte { + return append([]byte("shouldkey"), []byte(suffix)...) +} + +func TestSudo(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) + wmKeeper := mock_types.NewMockWasmKeeper(ctrl) + middleware, infCtx, storeKey := test_keeper.NewSudoLimitWrapper(t, cmKeeper, wmKeeper) + st := infCtx.KVStore(storeKey) + + // at this point the payload struct does not matter + msg := []byte("sudo_payload") + contractAddress := sdk.AccAddress{} + + // success during Sudo + ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) + cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 10000}) + wmKeeper.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddress, msg).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, msg []byte) { + st := cachedCtx.KVStore(storeKey) + st.Set(ShouldBeWrittenKey("sudo"), ShouldBeWritten) + }).Return(nil, nil) + _, err := middleware.Sudo(ctx, contractAddress, msg) + require.NoError(t, err) + require.Equal(t, ShouldBeWritten, st.Get(ShouldBeWrittenKey("sudo"))) + + // error during Sudo + ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) + cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 10000}) + cmKeeper.EXPECT().AddContractFailure(ctx, contractAddress.String(), msg) + wmKeeper.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddress, msg).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, msg []byte) { + st := cachedCtx.KVStore(storeKey) + st.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) + }).Return(nil, fmt.Errorf("sudo error")) + _, err = middleware.Sudo(ctx, contractAddress, msg) + require.Error(t, err) + require.Nil(t, st.Get(ShouldNotBeWrittenKey)) + + // ou of gas during Sudo + ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) + cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 10000}) + cmKeeper.EXPECT().AddContractFailure(ctx, contractAddress.String(), msg) + wmKeeper.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddress, msg).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, msg []byte) { + st := cachedCtx.KVStore(storeKey) + st.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) + cachedCtx.GasMeter().ConsumeGas(10001, "heavy calculations") + }) + _, err = middleware.Sudo(ctx, contractAddress, msg) + require.ErrorContains(t, err, "{heavy calculations}: panic") + require.Nil(t, st.Get(ShouldNotBeWrittenKey)) +} diff --git a/x/contractmanager/keeper/failure.go b/x/contractmanager/keeper/failure.go index 308decb34..02e653655 100644 --- a/x/contractmanager/keeper/failure.go +++ b/x/contractmanager/keeper/failure.go @@ -5,17 +5,14 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" "github.com/neutron-org/neutron/x/contractmanager/types" ) // AddContractFailure adds a specific failure to the store using address as the key -func (k Keeper) AddContractFailure(ctx sdk.Context, packet *ibcchanneltypes.Packet, address, ackType string, ack *ibcchanneltypes.Acknowledgement) { +func (k Keeper) AddContractFailure(ctx sdk.Context, address string, sudoPayload []byte) { failure := types.Failure{ - Address: address, - AckType: ackType, - Packet: packet, - Ack: ack, + Address: address, + SudoPayload: sudoPayload, } nextFailureID := k.GetNextFailureIDKey(ctx, failure.GetAddress()) failure.Id = nextFailureID @@ -40,7 +37,7 @@ func (k Keeper) GetNextFailureIDKey(ctx sdk.Context, address string) uint64 { return 0 } -// GetAllFailure returns all failures +// GetAllFailures returns all failures func (k Keeper) GetAllFailures(ctx sdk.Context) (list []types.Failure) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.ContractFailuresKey) iterator := sdk.KVStorePrefixIterator(store, []byte{}) @@ -71,30 +68,12 @@ func (k Keeper) GetFailure(ctx sdk.Context, contractAddr sdk.AccAddress, id uint // ResubmitFailure tries to call sudo handler for contract with same parameters as initially. func (k Keeper) ResubmitFailure(ctx sdk.Context, contractAddr sdk.AccAddress, failure *types.Failure) error { - if failure.Packet == nil { - return errors.Wrapf(types.IncorrectFailureToResubmit, "cannot resubmit failure without packet info; failureId = %d", failure.Id) + if failure.SudoPayload == nil { + return errors.Wrapf(types.IncorrectFailureToResubmit, "cannot resubmit failure without sudo payload; failureId = %d", failure.Id) } - switch failure.GetAckType() { - case types.Ack: - if failure.GetAck() == nil { - return errors.Wrapf(types.IncorrectFailureToResubmit, "cannot resubmit failure without acknowledgement; failureId = %d", failure.Id) - } - if failure.GetAck().GetError() == "" { - if _, err := k.SudoResponse(ctx, contractAddr, *failure.Packet, failure.Ack.GetResult()); err != nil { - return errors.Wrapf(types.FailedToResubmitFailure, "cannot resubmit failure ack response; failureId = %d; err = %s", failure.Id, err) - } - } else { - if _, err := k.SudoError(ctx, contractAddr, *failure.Packet, failure.Ack.GetError()); err != nil { - return errors.Wrapf(types.FailedToResubmitFailure, "cannot resubmit failure ack error; failureId = %d; err = %s", failure.Id, err) - } - } - case types.Timeout: - if _, err := k.SudoTimeout(ctx, contractAddr, *failure.Packet); err != nil { - return errors.Wrapf(types.FailedToResubmitFailure, "cannot resubmit failure ack timeout; failureId = %d; err = %s", failure.Id, err) - } - default: - return errors.Wrapf(types.IncorrectAckType, "cannot resubmit failure with incorrect ackType = %s", failure.GetAckType()) + if _, err := k.wasmKeeper.Sudo(ctx, contractAddr, failure.SudoPayload); err != nil { + return errors.Wrapf(types.FailedToResubmitFailure, "cannot resubmit failure; failureId = %d; err = %s", failure.Id, err) } // Cleanup failure since we resubmitted it successfully diff --git a/x/contractmanager/keeper/failure_test.go b/x/contractmanager/keeper/failure_test.go index 606db61c9..5edd064c3 100644 --- a/x/contractmanager/keeper/failure_test.go +++ b/x/contractmanager/keeper/failure_test.go @@ -26,7 +26,7 @@ import ( // Prevent strconv unused error var _ = strconv.IntSize -func createNFailure(keeper *keeper.Keeper, ctx sdk.Context, addresses, failures int) [][]types.Failure { +func createNFailure(k *keeper.Keeper, ctx sdk.Context, addresses, failures int) [][]types.Failure { pubBz := make([]byte, ed25519.PubKeySize) pub := &ed25519.PubKey{Key: pubBz} @@ -43,9 +43,12 @@ func createNFailure(keeper *keeper.Keeper, ctx sdk.Context, addresses, failures } items[i][c].Address = acc.String() items[i][c].Id = uint64(c) - items[i][c].Packet = &p - items[i][c].Ack = nil - keeper.AddContractFailure(ctx, &p, items[i][c].Address, "", nil) + sudo, err := keeper.PrepareSudoCallbackMessage(p, nil) + if err != nil { + panic(err) + } + items[i][c].SudoPayload = sudo + k.AddContractFailure(ctx, items[i][c].Address, sudo) } } return items @@ -83,11 +86,12 @@ func TestAddGetFailure(t *testing.T) { contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) k, ctx := keepertest.ContractManagerKeeper(t, nil) failureID := k.GetNextFailureIDKey(ctx, contractAddress.String()) - k.AddContractFailure(ctx, &channeltypes.Packet{}, contractAddress.String(), types.Ack, &channeltypes.Acknowledgement{}) + sudoPayload := []byte("payload") + k.AddContractFailure(ctx, contractAddress.String(), sudoPayload) failure, err := k.GetFailure(ctx, contractAddress, failureID) require.NoError(t, err) require.Equal(t, failureID, failure.Id) - require.Equal(t, types.Ack, failure.AckType) + require.Equal(t, sudoPayload, failure.SudoPayload) // non-existent id _, err = k.GetFailure(ctx, contractAddress, failureID+1) @@ -117,29 +121,31 @@ func TestResubmitFailure(t *testing.T) { // add ack failure packet := channeltypes.Packet{} failureID := k.GetNextFailureIDKey(ctx, contractAddr.String()) - k.AddContractFailure(ctx, &packet, contractAddr.String(), types.Ack, &ack) + payload, err := keeper.PrepareSudoCallbackMessage(packet, &ack) + require.NoError(t, err) + k.AddContractFailure(ctx, contractAddr.String(), payload) // success response - xSuc := types.MessageResponse{} - xSuc.Response.Data = data - xSuc.Response.Request = channeltypes.Packet{} + xSuc := types.MessageSudoCallback{Response: &types.ResponseSudoPayload{ + Request: channeltypes.Packet{}, + Data: data, + }} msgSuc, err := json.Marshal(xSuc) require.NoError(t, err) // error response - xErr := types.MessageError{} - xErr.Error.Request = channeltypes.Packet{} - xErr.Error.Details = "not able to do IBC tx" + xErr := types.MessageSudoCallback{Error: &types.ErrorSudoPayload{ + Request: channeltypes.Packet{}, + Details: "not able to do IBC tx", + }} msgErr, err := json.Marshal(xErr) require.NoError(t, err) // timeout response - xTimeout := types.MessageTimeout{} - xTimeout.Timeout.Request = channeltypes.Packet{} + xTimeout := types.MessageSudoCallback{Timeout: &types.TimeoutPayload{Request: channeltypes.Packet{}}} msgTimeout, err := json.Marshal(xTimeout) require.NoError(t, err) // case: successful resubmit with ack and ack = response - wk.EXPECT().HasContractInfo(gomock.AssignableToTypeOf(ctx), contractAddr).Return(true) - wk.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgSuc).Return([]byte{}, nil) + wk.EXPECT().Sudo(ctx, contractAddr, msgSuc).Return([]byte{}, nil) failure, err := k.GetFailure(ctx, contractAddr, failureID) require.NoError(t, err) @@ -151,15 +157,16 @@ func TestResubmitFailure(t *testing.T) { // case: failed resubmit with ack and ack = response failureID2 := k.GetNextFailureIDKey(ctx, contractAddr.String()) - k.AddContractFailure(ctx, &packet, contractAddr.String(), types.Ack, &ack) + payload, err = keeper.PrepareSudoCallbackMessage(packet, &ack) + require.NoError(t, err) + k.AddContractFailure(ctx, contractAddr.String(), payload) - wk.EXPECT().HasContractInfo(gomock.AssignableToTypeOf(ctx), contractAddr).Return(true) - wk.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgSuc).Return(nil, fmt.Errorf("failed to Sudo")) + wk.EXPECT().Sudo(ctx, contractAddr, msgSuc).Return(nil, fmt.Errorf("failed to sudo")) failure2, err := k.GetFailure(ctx, contractAddr, failureID2) require.NoError(t, err) err = k.ResubmitFailure(ctx, contractAddr, failure2) - require.ErrorContains(t, err, "cannot resubmit failure ack response") + require.ErrorContains(t, err, "cannot resubmit failure ack") // failure is still there failureAfter2, err := k.GetFailure(ctx, contractAddr, failureID2) require.NoError(t, err) @@ -168,9 +175,10 @@ func TestResubmitFailure(t *testing.T) { // case: successful resubmit with ack and ack = error // add error failure failureID3 := k.GetNextFailureIDKey(ctx, contractAddr.String()) - k.AddContractFailure(ctx, &packet, contractAddr.String(), types.Ack, &ackError) + payload, err = keeper.PrepareSudoCallbackMessage(packet, &ackError) + require.NoError(t, err) + k.AddContractFailure(ctx, contractAddr.String(), payload) - wk.EXPECT().HasContractInfo(gomock.AssignableToTypeOf(ctx), contractAddr).Return(true) wk.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgErr).Return([]byte{}, nil) failure3, err := k.GetFailure(ctx, contractAddr, failureID3) @@ -183,15 +191,16 @@ func TestResubmitFailure(t *testing.T) { // case: failed resubmit with ack and ack = error failureID4 := k.GetNextFailureIDKey(ctx, contractAddr.String()) - k.AddContractFailure(ctx, &packet, contractAddr.String(), types.Ack, &ackError) + payload, err = keeper.PrepareSudoCallbackMessage(packet, &ackError) + require.NoError(t, err) + k.AddContractFailure(ctx, contractAddr.String(), payload) - wk.EXPECT().HasContractInfo(gomock.AssignableToTypeOf(ctx), contractAddr).Return(true) - wk.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgErr).Return(nil, fmt.Errorf("failed to Sudo")) + wk.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgErr).Return(nil, fmt.Errorf("failed to sudo")) failure4, err := k.GetFailure(ctx, contractAddr, failureID4) require.NoError(t, err) err = k.ResubmitFailure(ctx, contractAddr, failure4) - require.ErrorContains(t, err, "cannot resubmit failure ack error") + require.ErrorContains(t, err, "cannot resubmit failure ack") // failure is still there failureAfter4, err := k.GetFailure(ctx, contractAddr, failureID4) require.NoError(t, err) @@ -200,9 +209,10 @@ func TestResubmitFailure(t *testing.T) { // case: successful resubmit with timeout // add error failure failureID5 := k.GetNextFailureIDKey(ctx, contractAddr.String()) - k.AddContractFailure(ctx, &packet, contractAddr.String(), "timeout", nil) + payload, err = keeper.PrepareSudoCallbackMessage(packet, nil) + require.NoError(t, err) + k.AddContractFailure(ctx, contractAddr.String(), payload) - wk.EXPECT().HasContractInfo(gomock.AssignableToTypeOf(ctx), contractAddr).Return(true) wk.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgTimeout).Return([]byte{}, nil) failure5, err := k.GetFailure(ctx, contractAddr, failureID5) @@ -215,26 +225,18 @@ func TestResubmitFailure(t *testing.T) { // case: failed resubmit with timeout failureID6 := k.GetNextFailureIDKey(ctx, contractAddr.String()) - k.AddContractFailure(ctx, &packet, contractAddr.String(), "timeout", nil) + payload, err = keeper.PrepareSudoCallbackMessage(packet, nil) + require.NoError(t, err) + k.AddContractFailure(ctx, contractAddr.String(), payload) - wk.EXPECT().HasContractInfo(gomock.AssignableToTypeOf(ctx), contractAddr).Return(true) - wk.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgTimeout).Return(nil, fmt.Errorf("failed to Sudo")) + wk.EXPECT().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgTimeout).Return(nil, fmt.Errorf("failed to sudo")) failure6, err := k.GetFailure(ctx, contractAddr, failureID6) require.NoError(t, err) err = k.ResubmitFailure(ctx, contractAddr, failure6) - require.ErrorContains(t, err, "cannot resubmit failure ack timeout") + require.ErrorContains(t, err, "cannot resubmit failure ack") // failure is still there failureAfter6, err := k.GetFailure(ctx, contractAddr, failureID6) require.NoError(t, err) require.Equal(t, failureAfter6.Id, failure6.Id) - - // no Failure.Ack field found for ackType = 'ack' - failureID7 := k.GetNextFailureIDKey(ctx, contractAddr.String()) - k.AddContractFailure(ctx, &packet, contractAddr.String(), types.Ack, nil) - - failure7, err := k.GetFailure(ctx, contractAddr, failureID7) - require.NoError(t, err) - err = k.ResubmitFailure(ctx, contractAddr, failure7) - require.ErrorContains(t, err, "cannot resubmit failure without acknowledgement") } diff --git a/x/contractmanager/keeper/sudo.go b/x/contractmanager/keeper/sudo.go index 2feba728a..13a5e9e50 100644 --- a/x/contractmanager/keeper/sudo.go +++ b/x/contractmanager/keeper/sudo.go @@ -20,150 +20,37 @@ func (k Keeper) HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) return k.wasmKeeper.HasContractInfo(ctx, contractAddress) } -func (k Keeper) SudoResponse( - ctx sdk.Context, - senderAddress sdk.AccAddress, - request channeltypes.Packet, - msg []byte, -) ([]byte, error) { - k.Logger(ctx).Debug("SudoResponse", "senderAddress", senderAddress, "request", request, "msg", msg) - - if !k.wasmKeeper.HasContractInfo(ctx, senderAddress) { - if request.SourcePort == types.TransferPort { - // we want to allow non contract account to send the assets via IBC Transfer module - // we can determine the originating module by the source port of the request packet - return nil, nil +func PrepareSudoCallbackMessage(request channeltypes.Packet, ack *channeltypes.Acknowledgement) ([]byte, error) { + m := types.MessageSudoCallback{} + if ack != nil && ack.GetError() == "" { + m.Response = &types.ResponseSudoPayload{ + Data: ack.GetResult(), + Request: request, } - k.Logger(ctx).Debug("SudoResponse: contract not found", "senderAddress", senderAddress) - return nil, fmt.Errorf("%s is not a contract address and not the Transfer module", senderAddress) - } - - x := types.MessageResponse{} - x.Response.Data = msg - x.Response.Request = request - m, err := json.Marshal(x) - if err != nil { - k.Logger(ctx).Error("SudoResponse: failed to marshal MessageResponse message", - "error", err, "request", request, "contract_address", senderAddress) - return nil, fmt.Errorf("failed to marshal MessageResponse: %v", err) - } - - resp, err := k.wasmKeeper.Sudo(ctx, senderAddress, m) - if err != nil { - k.Logger(ctx).Debug("SudoResponse: failed to Sudo", - "error", err, "contract_address", senderAddress) - return nil, fmt.Errorf("failed to Sudo: %v", err) - } - - return resp, nil -} - -func (k Keeper) SudoTimeout( - ctx sdk.Context, - senderAddress sdk.AccAddress, - request channeltypes.Packet, -) ([]byte, error) { - k.Logger(ctx).Info("SudoTimeout", "senderAddress", senderAddress, "request", request) - - if !k.wasmKeeper.HasContractInfo(ctx, senderAddress) { - if request.SourcePort == types.TransferPort { - // we want to allow non contract account to send the assets via IBC Transfer module - // we can determine the originating module by the source port of the request packet - return nil, nil + } else if ack != nil { + m.Error = &types.ErrorSudoPayload{ + Request: request, + Details: ack.GetError(), } - k.Logger(ctx).Debug("SudoTimeout: contract not found", "senderAddress", senderAddress) - return nil, fmt.Errorf("%s is not a contract address and not the Transfer module", senderAddress) - } - - x := types.MessageTimeout{} - x.Timeout.Request = request - m, err := json.Marshal(x) - if err != nil { - k.Logger(ctx).Error("failed to marshal MessageTimeout message", - "error", err, "request", request, "contract_address", senderAddress) - return nil, fmt.Errorf("failed to marshal MessageTimeout: %v", err) + } else { + m.Timeout = &types.TimeoutPayload{Request: request} } - - k.Logger(ctx).Info("SudoTimeout sending request", "data", string(m)) - - resp, err := k.wasmKeeper.Sudo(ctx, senderAddress, m) + data, err := json.Marshal(m) if err != nil { - k.Logger(ctx).Debug("SudoTimeout: failed to Sudo", - "error", err, "contract_address", senderAddress) - return nil, fmt.Errorf("failed to Sudo: %v", err) + return nil, fmt.Errorf("failed to marshal MessageSudoCallback: %v", err) } - - return resp, nil + return data, nil } -func (k Keeper) SudoError( - ctx sdk.Context, - senderAddress sdk.AccAddress, - request channeltypes.Packet, - details string, -) ([]byte, error) { - k.Logger(ctx).Debug("SudoError", "senderAddress", senderAddress, "request", request) - - if !k.wasmKeeper.HasContractInfo(ctx, senderAddress) { - if request.SourcePort == types.TransferPort { - // we want to allow non contract account to send the assets via IBC Transfer module - // we can determine the originating module by the source port of the request packet - return nil, nil - } - k.Logger(ctx).Debug("SudoError: contract not found", "senderAddress", senderAddress) - return nil, fmt.Errorf("%s is not a contract address and not the Transfer module", senderAddress) - } - - x := types.MessageError{} - x.Error.Request = request - x.Error.Details = details - m, err := json.Marshal(x) - if err != nil { - k.Logger(ctx).Error("SudoError: failed to marshal MessageError message", - "error", err, "contract_address", senderAddress, "request", request) - return nil, fmt.Errorf("failed to marshal MessageError: %v", err) - } - - resp, err := k.wasmKeeper.Sudo(ctx, senderAddress, m) - if err != nil { - k.Logger(ctx).Debug("SudoError: failed to Sudo", - "error", err, "contract_address", senderAddress) - return nil, fmt.Errorf("failed to Sudo: %v", err) - } - - return resp, nil -} - -func (k Keeper) SudoOnChanOpenAck( - ctx sdk.Context, - contractAddress sdk.AccAddress, - details types.OpenAckDetails, -) ([]byte, error) { - k.Logger(ctx).Debug("SudoOnChanOpenAck", "contractAddress", contractAddress) - - if !k.wasmKeeper.HasContractInfo(ctx, contractAddress) { - k.Logger(ctx).Debug("SudoOnChanOpenAck: contract not found", "contractAddress", contractAddress) - return nil, fmt.Errorf("%s is not a contract address", contractAddress) +func PrepareOpenAckCallbackMessage(details types.OpenAckDetails) ([]byte, error) { + x := types.MessageOnChanOpenAck{ + OpenAck: details, } - - x := types.MessageOnChanOpenAck{} - x.OpenAck = details m, err := json.Marshal(x) if err != nil { - k.Logger(ctx).Error("SudoOnChanOpenAck: failed to marshal MessageOnChanOpenAck message", - "error", err, "contract_address", contractAddress) return nil, fmt.Errorf("failed to marshal MessageOnChanOpenAck: %v", err) } - k.Logger(ctx).Info("SudoOnChanOpenAck sending request", "data", string(m)) - - resp, err := k.wasmKeeper.Sudo(ctx, contractAddress, m) - if err != nil { - k.Logger(ctx).Debug("SudoOnChanOpenAck: failed to Sudo", - "error", err, "contract_address", contractAddress) - return nil, fmt.Errorf("failed to Sudo: %v", err) - } - - return resp, nil + return m, nil } // SudoTxQueryResult is used to pass a tx query result to the contract that registered the query @@ -198,9 +85,9 @@ func (k Keeper) SudoTxQueryResult( resp, err := k.wasmKeeper.Sudo(ctx, contractAddress, m) if err != nil { - k.Logger(ctx).Debug("SudoTxQueryResult: failed to Sudo", + k.Logger(ctx).Debug("SudoTxQueryResult: failed to sudo", "error", err, "contract_address", contractAddress) - return nil, fmt.Errorf("failed to Sudo: %v", err) + return nil, fmt.Errorf("failed to sudo: %v", err) } return resp, nil @@ -232,9 +119,9 @@ func (k Keeper) SudoKVQueryResult( resp, err := k.wasmKeeper.Sudo(ctx, contractAddress, m) if err != nil { - k.Logger(ctx).Debug("SudoKVQueryResult: failed to Sudo", + k.Logger(ctx).Debug("SudoKVQueryResult: failed to sudo", "error", err, "contract_address", contractAddress) - return nil, fmt.Errorf("failed to Sudo: %v", err) + return nil, fmt.Errorf("failed to sudo: %v", err) } return resp, nil diff --git a/x/contractmanager/keeper/sudo_test.go b/x/contractmanager/keeper/sudo_test.go index b4adc4103..12862b4de 100644 --- a/x/contractmanager/keeper/sudo_test.go +++ b/x/contractmanager/keeper/sudo_test.go @@ -6,7 +6,6 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -21,168 +20,6 @@ func init() { app.GetDefaultConfig() } -func TestSudoHasAddress(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - wk := mock_types.NewMockWasmKeeper(ctrl) - - k, ctx := keepertest.ContractManagerKeeper(t, wk) - address := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - has := k.HasContractInfo(ctx, address) - require.Equal(t, true, has) - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(false) - has = k.HasContractInfo(ctx, address) - require.Equal(t, false, has) -} - -func TestSudoResponse(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - wk := mock_types.NewMockWasmKeeper(ctrl) - - k, ctx := keepertest.ContractManagerKeeper(t, wk) - address := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) - - sudoErrorMsg := types.MessageResponse{} - p := channeltypes.Packet{} - sudoErrorMsg.Response.Data = []byte("data") - sudoErrorMsg.Response.Request = p - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoErrorMsg)).Return([]byte("success"), nil) - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - resp, err := k.SudoResponse(ctx, address, sudoErrorMsg.Response.Request, sudoErrorMsg.Response.Data) - require.NoError(t, err) - require.Equal(t, []byte("success"), resp) - - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoErrorMsg)).Return(nil, fmt.Errorf("internal contract error")) - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - resp, err = k.SudoResponse(ctx, address, sudoErrorMsg.Response.Request, sudoErrorMsg.Response.Data) - require.Nil(t, resp) - require.ErrorContains(t, err, "internal contract error") - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(false) - resp, err = k.SudoResponse(ctx, address, channeltypes.Packet{}, nil) - require.Nil(t, resp) - require.ErrorContains(t, err, "is not a contract address and not the Transfer module") - - sudoResponseTransport := types.MessageResponse{} - p = channeltypes.Packet{SourcePort: types.TransferPort} - sudoResponseTransport.Response.Data = []byte("data") - sudoResponseTransport.Response.Request = p - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(false) - _, err = k.SudoResponse(ctx, address, sudoResponseTransport.Response.Request, sudoResponseTransport.Response.Data) - require.Nil(t, err) - require.NoError(t, err) -} - -func TestSudoError(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - wk := mock_types.NewMockWasmKeeper(ctrl) - - k, ctx := keepertest.ContractManagerKeeper(t, wk) - address := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) - - sudoErrorMsg := types.MessageError{} - p := channeltypes.Packet{} - sudoErrorMsg.Error.Details = "details" - sudoErrorMsg.Error.Request = p - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoErrorMsg)).Return([]byte("success"), nil) - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - resp, err := k.SudoError(ctx, address, sudoErrorMsg.Error.Request, sudoErrorMsg.Error.Details) - require.NoError(t, err) - require.Equal(t, []byte("success"), resp) - - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoErrorMsg)).Return(nil, fmt.Errorf("internal contract error")) - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - resp, err = k.SudoError(ctx, address, sudoErrorMsg.Error.Request, sudoErrorMsg.Error.Details) - require.Nil(t, resp) - require.ErrorContains(t, err, "internal contract error") - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(false) - resp, err = k.SudoError(ctx, address, channeltypes.Packet{}, "") - require.Nil(t, resp) - require.ErrorContains(t, err, "is not a contract address and not the Transfer module") - - sudoErrorTransport := types.MessageError{} - p = channeltypes.Packet{SourcePort: types.TransferPort} - sudoErrorTransport.Error.Details = "details" - sudoErrorTransport.Error.Request = p - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(false) - resp, err = k.SudoError(ctx, address, sudoErrorTransport.Error.Request, sudoErrorTransport.Error.Details) - require.Nil(t, resp) - require.NoError(t, err) -} - -func TestSudoTimeout(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - wk := mock_types.NewMockWasmKeeper(ctrl) - - k, ctx := keepertest.ContractManagerKeeper(t, wk) - address := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) - - sudoTimeoutMsg := types.MessageTimeout{} - p := channeltypes.Packet{} - sudoTimeoutMsg.Timeout.Request = p - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoTimeoutMsg)).Return([]byte("success"), nil) - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - resp, err := k.SudoTimeout(ctx, address, sudoTimeoutMsg.Timeout.Request) - require.NoError(t, err) - require.Equal(t, []byte("success"), resp) - - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoTimeoutMsg)).Return(nil, fmt.Errorf("internal contract error")) - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - resp, err = k.SudoTimeout(ctx, address, sudoTimeoutMsg.Timeout.Request) - require.Nil(t, resp) - require.ErrorContains(t, err, "internal contract error") - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(false) - resp, err = k.SudoTimeout(ctx, address, channeltypes.Packet{}) - require.Nil(t, resp) - require.ErrorContains(t, err, "is not a contract address and not the Transfer module") - - sudoTimeoutTransport := types.MessageTimeout{} - p = channeltypes.Packet{SourcePort: types.TransferPort} - sudoTimeoutTransport.Timeout.Request = p - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(false) - resp, err = k.SudoTimeout(ctx, address, sudoTimeoutTransport.Timeout.Request) - require.Nil(t, resp) - require.NoError(t, err) -} - -func TestSudoOnChanOpen(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - wk := mock_types.NewMockWasmKeeper(ctrl) - - k, ctx := keepertest.ContractManagerKeeper(t, wk) - address := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) - - sudoOpenAckMsg := types.MessageOnChanOpenAck{} - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoOpenAckMsg)).Return([]byte("success"), nil) - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - resp, err := k.SudoOnChanOpenAck(ctx, address, sudoOpenAckMsg.OpenAck) - require.NoError(t, err) - require.Equal(t, []byte("success"), resp) - - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoOpenAckMsg)).Return(nil, fmt.Errorf("internal contract error")) - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(true) - resp, err = k.SudoOnChanOpenAck(ctx, address, sudoOpenAckMsg.OpenAck) - require.Nil(t, resp) - require.ErrorContains(t, err, "internal contract error") - - wk.EXPECT().HasContractInfo(gomock.Any(), address).Return(false) - resp, err = k.SudoOnChanOpenAck(ctx, address, sudoOpenAckMsg.OpenAck) - require.Nil(t, resp) - require.ErrorContains(t, err, "is not a contract address") -} - func TestSudoTxQueryResult(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/x/contractmanager/types/expected_keepers.go b/x/contractmanager/types/expected_keepers.go index a30614c63..88db20887 100644 --- a/x/contractmanager/types/expected_keepers.go +++ b/x/contractmanager/types/expected_keepers.go @@ -9,3 +9,8 @@ type WasmKeeper interface { HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) } + +type ContractManagerKeeper interface { + AddContractFailure(ctx sdk.Context, address string, sudoPayload []byte) + GetParams(ctx sdk.Context) (params Params) +} diff --git a/x/contractmanager/types/failure.pb.go b/x/contractmanager/types/failure.pb.go index abc19b1a4..063afa74f 100644 --- a/x/contractmanager/types/failure.pb.go +++ b/x/contractmanager/types/failure.pb.go @@ -6,7 +6,7 @@ package types import ( fmt "fmt" proto "github.com/cosmos/gogoproto/proto" - types "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + _ "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" io "io" math "math" math_bits "math/bits" @@ -31,12 +31,8 @@ type Failure struct { Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // Id of the failure under specific address Id uint64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` - // Acknowledgement type - AckType string `protobuf:"bytes,3,opt,name=ack_type,json=ackType,proto3" json:"ack_type,omitempty"` - // IBC Packet - Packet *types.Packet `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"` - // Acknowledgement - Ack *types.Acknowledgement `protobuf:"bytes,5,opt,name=ack,proto3" json:"ack,omitempty"` + // Serialized MessageSudoCallback with Packet and Ack(if exists) + SudoPayload []byte `protobuf:"bytes,3,opt,name=sudo_payload,json=sudoPayload,proto3" json:"sudo_payload,omitempty"` } func (m *Failure) Reset() { *m = Failure{} } @@ -86,23 +82,9 @@ func (m *Failure) GetId() uint64 { return 0 } -func (m *Failure) GetAckType() string { +func (m *Failure) GetSudoPayload() []byte { if m != nil { - return m.AckType - } - return "" -} - -func (m *Failure) GetPacket() *types.Packet { - if m != nil { - return m.Packet - } - return nil -} - -func (m *Failure) GetAck() *types.Acknowledgement { - if m != nil { - return m.Ack + return m.SudoPayload } return nil } @@ -116,25 +98,22 @@ func init() { } var fileDescriptor_fba0c26e85dad46e = []byte{ - // 288 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xb1, 0x4e, 0xeb, 0x30, - 0x14, 0x86, 0xeb, 0xb6, 0xb7, 0xbd, 0x18, 0x89, 0xc1, 0x0b, 0x06, 0x24, 0xab, 0x20, 0x90, 0xba, - 0x60, 0xab, 0x54, 0xea, 0x0e, 0x03, 0x73, 0x15, 0x31, 0xb1, 0x20, 0xe7, 0xe4, 0x90, 0x5a, 0x69, - 0xed, 0xc8, 0x75, 0x0b, 0x7d, 0x0b, 0xde, 0x87, 0x17, 0x60, 0xec, 0xc8, 0x88, 0x9a, 0x17, 0x41, - 0x49, 0x93, 0x05, 0xd8, 0x7e, 0x4b, 0xdf, 0xe7, 0x73, 0xce, 0x4f, 0xaf, 0x2c, 0xae, 0x82, 0x77, - 0x56, 0x81, 0xb3, 0xc1, 0x6b, 0x08, 0x0b, 0x6d, 0x75, 0x8a, 0x5e, 0x3d, 0x6b, 0x33, 0x5f, 0x79, - 0x94, 0xb9, 0x77, 0xc1, 0xb1, 0xe3, 0x1a, 0x93, 0x3f, 0xb0, 0xd3, 0x73, 0x13, 0x83, 0x02, 0xe7, - 0x51, 0xc1, 0x4c, 0x5b, 0x8b, 0x73, 0xb5, 0x1e, 0x35, 0x71, 0xef, 0x5e, 0xbc, 0x13, 0xda, 0xbf, - 0xdf, 0xff, 0xc6, 0x38, 0xed, 0xeb, 0x24, 0xf1, 0xb8, 0x5c, 0x72, 0x32, 0x20, 0xc3, 0x83, 0xa8, - 0x79, 0xb2, 0x23, 0xda, 0x36, 0x09, 0x6f, 0x0f, 0xc8, 0xb0, 0x1b, 0xb5, 0x4d, 0xc2, 0x4e, 0xe8, - 0x7f, 0x0d, 0xd9, 0x53, 0xd8, 0xe4, 0xc8, 0x3b, 0x35, 0x0a, 0xd9, 0xc3, 0x26, 0x47, 0x36, 0xa6, - 0xbd, 0x5c, 0x43, 0x86, 0x81, 0x77, 0x07, 0x64, 0x78, 0x78, 0x73, 0x26, 0x4d, 0x0c, 0xb2, 0x5c, - 0x42, 0x36, 0x93, 0xd7, 0x23, 0x39, 0xad, 0x90, 0xa8, 0x46, 0xd9, 0x84, 0x76, 0x34, 0x64, 0xfc, - 0x5f, 0x65, 0x5c, 0xfe, 0x69, 0xdc, 0x42, 0x66, 0xdd, 0xcb, 0x1c, 0x93, 0x14, 0x17, 0x68, 0x43, - 0x54, 0x0a, 0x77, 0xd3, 0x8f, 0x9d, 0x20, 0xdb, 0x9d, 0x20, 0x5f, 0x3b, 0x41, 0xde, 0x0a, 0xd1, - 0xda, 0x16, 0xa2, 0xf5, 0x59, 0x88, 0xd6, 0xe3, 0x24, 0x35, 0x61, 0xb6, 0x8a, 0x25, 0xb8, 0x85, - 0xaa, 0xeb, 0xb9, 0x76, 0x3e, 0x6d, 0xb2, 0x7a, 0xfd, 0xd5, 0x69, 0x79, 0xcc, 0x32, 0xee, 0x55, - 0xb5, 0x8c, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x40, 0x42, 0xfd, 0x80, 0x7b, 0x01, 0x00, 0x00, + // 230 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcd, 0x4b, 0x2d, 0x2d, + 0x29, 0xca, 0xcf, 0xd3, 0x4f, 0xce, 0xcf, 0x2b, 0x29, 0x4a, 0x4c, 0x2e, 0xc9, 0x4d, 0xcc, 0x4b, + 0x4c, 0x4f, 0x2d, 0xd2, 0x4f, 0x4b, 0xcc, 0xcc, 0x29, 0x2d, 0x4a, 0xd5, 0x2b, 0x28, 0xca, 0x2f, + 0xc9, 0x17, 0x12, 0x87, 0x2a, 0xd3, 0x43, 0x53, 0x26, 0xa5, 0x98, 0x99, 0x94, 0xac, 0x9f, 0x9c, + 0x5f, 0x94, 0xaa, 0x9f, 0x9c, 0x91, 0x98, 0x97, 0x97, 0x9a, 0xa3, 0x5f, 0x66, 0x08, 0x63, 0x42, + 0xf4, 0x2a, 0x85, 0x71, 0xb1, 0xbb, 0x41, 0x0c, 0x13, 0x92, 0xe0, 0x62, 0x4f, 0x4c, 0x49, 0x29, + 0x4a, 0x2d, 0x2e, 0x96, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x71, 0x85, 0xf8, 0xb8, 0x98, + 0x32, 0x53, 0x24, 0x98, 0x14, 0x18, 0x35, 0x58, 0x82, 0x98, 0x32, 0x53, 0x84, 0x14, 0xb9, 0x78, + 0x8a, 0x4b, 0x53, 0xf2, 0xe3, 0x0b, 0x12, 0x2b, 0x73, 0xf2, 0x13, 0x53, 0x24, 0x98, 0x15, 0x18, + 0x35, 0x78, 0x82, 0xb8, 0x41, 0x62, 0x01, 0x10, 0x21, 0xa7, 0x80, 0x13, 0x8f, 0xe4, 0x18, 0x2f, + 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, + 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, + 0xd5, 0x87, 0x3a, 0x5c, 0x37, 0xbf, 0x28, 0x1d, 0xc6, 0xd6, 0xaf, 0xc0, 0xf0, 0x6d, 0x49, 0x65, + 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0xc1, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc1, 0xf7, + 0xe5, 0xa7, 0x15, 0x01, 0x00, 0x00, } func (m *Failure) Marshal() (dAtA []byte, err error) { @@ -157,34 +136,10 @@ func (m *Failure) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Ack != nil { - { - size, err := m.Ack.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintFailure(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - } - if m.Packet != nil { - { - size, err := m.Packet.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintFailure(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - if len(m.AckType) > 0 { - i -= len(m.AckType) - copy(dAtA[i:], m.AckType) - i = encodeVarintFailure(dAtA, i, uint64(len(m.AckType))) + if len(m.SudoPayload) > 0 { + i -= len(m.SudoPayload) + copy(dAtA[i:], m.SudoPayload) + i = encodeVarintFailure(dAtA, i, uint64(len(m.SudoPayload))) i-- dAtA[i] = 0x1a } @@ -227,18 +182,10 @@ func (m *Failure) Size() (n int) { if m.Id != 0 { n += 1 + sovFailure(uint64(m.Id)) } - l = len(m.AckType) + l = len(m.SudoPayload) if l > 0 { n += 1 + l + sovFailure(uint64(l)) } - if m.Packet != nil { - l = m.Packet.Size() - n += 1 + l + sovFailure(uint64(l)) - } - if m.Ack != nil { - l = m.Ack.Size() - n += 1 + l + sovFailure(uint64(l)) - } return n } @@ -330,77 +277,9 @@ func (m *Failure) Unmarshal(dAtA []byte) error { } case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AckType", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowFailure - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthFailure - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthFailure - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AckType = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Packet", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowFailure - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthFailure - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthFailure - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Packet == nil { - m.Packet = &types.Packet{} - } - if err := m.Packet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Ack", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SudoPayload", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowFailure @@ -410,26 +289,24 @@ func (m *Failure) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthFailure } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthFailure } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Ack == nil { - m.Ack = &types.Acknowledgement{} - } - if err := m.Ack.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.SudoPayload = append(m.SudoPayload[:0], dAtA[iNdEx:postIndex]...) + if m.SudoPayload == nil { + m.SudoPayload = []byte{} } iNdEx = postIndex default: diff --git a/x/contractmanager/types/sudo.go b/x/contractmanager/types/sudo.go index c0564d82a..0670a1696 100644 --- a/x/contractmanager/types/sudo.go +++ b/x/contractmanager/types/sudo.go @@ -5,8 +5,6 @@ import ( channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) -const TransferPort = "transfer" - // MessageTxQueryResult is passed to a contract's sudo() entrypoint when a tx was submitted // for a transaction query. type MessageTxQueryResult struct { @@ -25,30 +23,26 @@ type MessageKVQueryResult struct { } `json:"kv_query_result"` } -// MessageTimeout is passed to a contract's sudo() entrypoint when an interchain -// transaction failed with a timeout. -type MessageTimeout struct { - Timeout struct { - Request channeltypes.Packet `json:"request"` - } `json:"timeout"` +// MessageSudoCallback is passed to a contract's sudo() entrypoint when an interchain +// transaction ended up with Success/Error or timed out. +type MessageSudoCallback struct { + Response *ResponseSudoPayload `json:"response,omitempty"` + Error *ErrorSudoPayload `json:"error,omitempty"` + Timeout *TimeoutPayload `json:"timeout,omitempty"` +} + +type ResponseSudoPayload struct { + Request channeltypes.Packet `json:"request"` + Data []byte `json:"data"` // Message data } -// MessageResponse is passed to a contract's sudo() entrypoint when an interchain -// transaction was executed successfully. -type MessageResponse struct { - Response struct { - Request channeltypes.Packet `json:"request"` - Data []byte `json:"data"` // Message data - } `json:"response"` +type ErrorSudoPayload struct { + Request channeltypes.Packet `json:"request"` + Details string `json:"details"` } -// MessageError is passed to a contract's sudo() entrypoint when an interchain -// transaction was executed with an error. -type MessageError struct { - Error struct { - Request channeltypes.Packet `json:"request"` - Details string `json:"details"` - } `json:"error"` +type TimeoutPayload struct { + Request channeltypes.Packet `json:"request"` } // MessageOnChanOpenAck is passed to a contract's sudo() entrypoint when an interchain diff --git a/x/interchainqueries/types/expected_keepers.go b/x/interchainqueries/types/expected_keepers.go index d0cb0332f..820259470 100644 --- a/x/interchainqueries/types/expected_keepers.go +++ b/x/interchainqueries/types/expected_keepers.go @@ -4,7 +4,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // AccountKeeper defines the expected account keeper used for simulations (noalias) @@ -23,9 +22,6 @@ type BankKeeper interface { type ContractManagerKeeper interface { HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool - SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) ([]byte, error) - SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, details string) ([]byte, error) - SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) SudoKVQueryResult(ctx sdk.Context, contractAddress sdk.AccAddress, queryID uint64) ([]byte, error) SudoTxQueryResult(ctx sdk.Context, contractAddress sdk.AccAddress, queryID uint64, height ibcclienttypes.Height, data []byte) ([]byte, error) } diff --git a/x/interchaintxs/genesis_test.go b/x/interchaintxs/genesis_test.go index 88cb68297..2adc3476d 100644 --- a/x/interchaintxs/genesis_test.go +++ b/x/interchaintxs/genesis_test.go @@ -16,7 +16,7 @@ func TestGenesis(t *testing.T) { Params: types.DefaultParams(), } - k, ctx, _ := keepertest.InterchainTxsKeeper(t, nil, nil, nil, nil) + k, ctx := keepertest.InterchainTxsKeeper(t, nil, nil, nil, nil, nil, nil) interchaintxs.InitGenesis(ctx, *k, genesisState) got := interchaintxs.ExportGenesis(ctx, *k) require.NotNil(t, got) diff --git a/x/interchaintxs/handler.go b/x/interchaintxs/handler.go deleted file mode 100644 index 30a574948..000000000 --- a/x/interchaintxs/handler.go +++ /dev/null @@ -1,34 +0,0 @@ -package interchaintxs - -import ( - "fmt" - - "cosmossdk.io/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/neutron-org/neutron/x/interchaintxs/keeper" - "github.com/neutron-org/neutron/x/interchaintxs/types" -) - -func NewHandler(k keeper.Keeper) sdk.Handler { - msgServer := keeper.NewMsgServerImpl(k) - - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgRegisterInterchainAccount: - res, err := msgServer.RegisterInterchainAccount(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - case *types.MsgSubmitTx: - res, err := msgServer.SubmitTx(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg) - return nil, errors.Wrap(sdkerrors.ErrUnknownRequest, errMsg) - } - } -} diff --git a/x/interchaintxs/keeper/grpc_query_interchainaccount_test.go b/x/interchaintxs/keeper/grpc_query_interchainaccount_test.go index 1c631682f..5de39ad40 100644 --- a/x/interchaintxs/keeper/grpc_query_interchainaccount_test.go +++ b/x/interchaintxs/keeper/grpc_query_interchainaccount_test.go @@ -20,7 +20,7 @@ func TestKeeper_InterchainAccountAddress(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) - keeper, ctx, _ := testkeeper.InterchainTxsKeeper(t, nil, nil, icaKeeper, nil) + keeper, ctx := testkeeper.InterchainTxsKeeper(t, nil, nil, icaKeeper, nil, nil, nil) wctx := sdk.WrapSDKContext(ctx) resp, err := keeper.InterchainAccountAddress(wctx, nil) diff --git a/x/interchaintxs/keeper/grpc_query_params_test.go b/x/interchaintxs/keeper/grpc_query_params_test.go index 90df327c3..5fa31e0c3 100644 --- a/x/interchaintxs/keeper/grpc_query_params_test.go +++ b/x/interchaintxs/keeper/grpc_query_params_test.go @@ -11,7 +11,7 @@ import ( ) func TestParamsQuery(t *testing.T) { - keeper, ctx, _ := testkeeper.InterchainTxsKeeper(t, nil, nil, nil, nil) + keeper, ctx := testkeeper.InterchainTxsKeeper(t, nil, nil, nil, nil, nil, nil) wctx := sdk.WrapSDKContext(ctx) params := types.DefaultParams() err := keeper.SetParams(ctx, params) diff --git a/x/interchaintxs/keeper/ibc_handlers.go b/x/interchaintxs/keeper/ibc_handlers.go index d1deab8fa..1540a8c9a 100644 --- a/x/interchaintxs/keeper/ibc_handlers.go +++ b/x/interchaintxs/keeper/ibc_handlers.go @@ -1,8 +1,7 @@ package keeper import ( - "github.com/neutron-org/neutron/neutronutils" - neutronerrors "github.com/neutron-org/neutron/neutronutils/errors" + "github.com/neutron-org/neutron/x/contractmanager/keeper" "time" "cosmossdk.io/errors" @@ -17,10 +16,9 @@ import ( "github.com/neutron-org/neutron/x/interchaintxs/types" ) -// HandleAcknowledgement passes the acknowledgement data to the appropriate contract via a Sudo call. +// HandleAcknowledgement passes the acknowledgement data to the appropriate contract via a sudo call. func (k *Keeper) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), LabelHandleAcknowledgment) - k.Logger(ctx).Debug("Handling acknowledgement") icaOwner, err := types.ICAOwnerFromPort(packet.SourcePort) if err != nil { @@ -33,63 +31,46 @@ func (k *Keeper) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.Pack k.Logger(ctx).Error("HandleAcknowledgement: cannot unmarshal ICS-27 packet acknowledgement", "error", err) return errors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-27 packet acknowledgement: %v", err) } + msg, err := keeper.PrepareSudoCallbackMessage(packet, &ack) + if err != nil { + return errors.Wrapf(sdkerrors.ErrJSONMarshal, "failed to marshal Packet/Acknowledgment: %v", err) + } k.feeKeeper.DistributeAcknowledgementFee(ctx, relayer, feetypes.NewPacketID(packet.SourcePort, packet.SourceChannel, packet.Sequence)) - cacheCtx, writeFn := neutronutils.CreateCachedContext(ctx, k.contractManagerKeeper.GetParams(ctx).SudoCallGasLimit) - func() { - defer neutronerrors.OutOfGasRecovery(cacheCtx.GasMeter(), &err) - // Actually we have only one kind of error returned from acknowledgement - // maybe later we'll retrieve actual errors from events - if ack.GetError() != "" { - - _, err = k.contractManagerKeeper.SudoError(cacheCtx, icaOwner.GetContract(), packet, ack.GetError()) - } else { - _, err = k.contractManagerKeeper.SudoResponse(cacheCtx, icaOwner.GetContract(), packet, ack.GetResult()) - } - }() + // Actually we have only one kind of error returned from acknowledgement + // maybe later we'll retrieve actual errors from events + _, err = k.sudoKeeper.Sudo(ctx, icaOwner.GetContract(), msg) if err != nil { - // the contract either returned an error or panicked with `out of gas` - k.contractManagerKeeper.AddContractFailure(ctx, &packet, icaOwner.GetContract().String(), contractmanagertypes.Ack, &ack) k.Logger(ctx).Debug("HandleAcknowledgement: failed to Sudo contract on packet acknowledgement", "error", err) - } else { - writeFn() } - ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") - return nil } -// HandleTimeout passes the timeout data to the appropriate contract via a Sudo call. +// HandleTimeout passes the timeout data to the appropriate contract via a sudo call. // Since all ICA channels are ORDERED, a single timeout shuts down a channel. func (k *Keeper) HandleTimeout(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), LabelHandleTimeout) - - icaOwner, err := types.ICAOwnerFromPort(packet.SourcePort) k.Logger(ctx).Debug("HandleTimeout") + icaOwner, err := types.ICAOwnerFromPort(packet.SourcePort) if err != nil { k.Logger(ctx).Error("HandleTimeout: failed to get ica owner from source port", "error", err) return errors.Wrap(err, "failed to get ica owner from port") } + msg, err := keeper.PrepareSudoCallbackMessage(packet, nil) + if err != nil { + return errors.Wrapf(sdkerrors.ErrJSONMarshal, "failed to marshal Packet: %v", err) + } + k.feeKeeper.DistributeTimeoutFee(ctx, relayer, feetypes.NewPacketID(packet.SourcePort, packet.SourceChannel, packet.Sequence)) - cacheCtx, writeFn := neutronutils.CreateCachedContext(ctx, k.contractManagerKeeper.GetParams(ctx).SudoCallGasLimit) - func() { - defer neutronerrors.OutOfGasRecovery(cacheCtx.GasMeter(), &err) - _, err = k.contractManagerKeeper.SudoTimeout(cacheCtx, icaOwner.GetContract(), packet) - }() + _, err = k.sudoKeeper.Sudo(ctx, icaOwner.GetContract(), msg) if err != nil { - // the contract either returned an error or panicked with `out of gas` - k.contractManagerKeeper.AddContractFailure(ctx, &packet, icaOwner.GetContract().String(), contractmanagertypes.Timeout, nil) - k.Logger(ctx).Error("HandleTimeout: failed to Sudo contract on packet timeout", "error", err) - } else { - writeFn() + k.Logger(ctx).Debug("HandleTimeout: failed to Sudo contract on packet timeout", "error", err) } - ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") - return nil } @@ -113,15 +94,16 @@ func (k *Keeper) HandleChanOpenAck( return errors.Wrap(err, "failed to get ica owner from port") } - _, err = k.contractManagerKeeper.SudoOnChanOpenAck(ctx, icaOwner.GetContract(), contractmanagertypes.OpenAckDetails{ + payload, err := keeper.PrepareOpenAckCallbackMessage(contractmanagertypes.OpenAckDetails{ PortID: portID, ChannelID: channelID, CounterpartyChannelID: counterpartyChannelID, CounterpartyVersion: counterpartyVersion, }) + + _, err = k.sudoKeeper.Sudo(ctx, icaOwner.GetContract(), payload) if err != nil { - k.Logger(ctx).Debug("HandleChanOpenAck: failed to Sudo contract on packet timeout", "error", err) - return errors.Wrap(err, "failed to Sudo the contract OnChanOpenAck") + k.Logger(ctx).Debug("HandleChanOpenAck: failed to sudo contract on channel open acknowledgement", "error", err) } return nil diff --git a/x/interchaintxs/keeper/ibc_handlers_test.go b/x/interchaintxs/keeper/ibc_handlers_test.go index 85f435d4b..c0a8c40b6 100644 --- a/x/interchaintxs/keeper/ibc_handlers_test.go +++ b/x/interchaintxs/keeper/ibc_handlers_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "fmt" + "github.com/neutron-org/neutron/x/contractmanager/keeper" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -17,33 +18,17 @@ import ( feetypes "github.com/neutron-org/neutron/x/feerefunder/types" ) -var ( - ShouldNotBeWrittenKey = []byte("shouldnotkey") - ShouldNotBeWritten = []byte("should not be written") - ShouldBeWritten = []byte("should be written") -) - -func ShouldBeWrittenKey(suffix string) []byte { - return append([]byte("shouldkey"), []byte(suffix)...) -} - func TestHandleAcknowledgement(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) - cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) + wmKeeper := mock_types.NewMockWasmKeeper(ctrl) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) - icak, infCtx, storeKey := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil) + bankKeeper := mock_types.NewMockBankKeeper(ctrl) + feeburnerKeeper := mock_types.NewMockFeeBurnerKeeper(ctrl) + icak, infCtx := testkeeper.InterchainTxsKeeper(t, wmKeeper, feeKeeper, icaKeeper, nil, bankKeeper, feeburnerKeeper) ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - store := ctx.KVStore(storeKey) - errACK := channeltypes.Acknowledgement{ - Response: &channeltypes.Acknowledgement_Error{ - Error: "error", - }, - } - errAckData, err := channeltypes.SubModuleCdc.MarshalJSON(&errACK) - require.NoError(t, err) resACK := channeltypes.Acknowledgement{ Response: &channeltypes.Acknowledgement_Result{Result: []byte("Result")}, } @@ -64,97 +49,34 @@ func TestHandleAcknowledgement(t *testing.T) { err = icak.HandleAcknowledgement(ctx, p, nil, relayerAddress) require.ErrorContains(t, err, "cannot unmarshal ICS-27 packet acknowledgement") - // error during SudoResponse - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(ctx), contractAddress, p, resACK.GetResult()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) // consumes 2990 - }).Return(nil, fmt.Errorf("SudoResponse error")) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 4000}) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Ack, &resACK) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) + msgAck, err := keeper.PrepareSudoCallbackMessage(p, &resACK) require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) - // error during SudoError + // success contract SudoResponse ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, err string) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - }).Return(nil, fmt.Errorf("SudoError error")) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 5000}) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Ack, &errACK) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = icak.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) - - // success during SudoError - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, err string) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldBeWrittenKey("sudoerror"), ShouldBeWritten) - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 6000}) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = icak.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.NoError(t, err) - require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudoerror"))) - require.Equal(t, uint64(3050), ctx.GasMeter().GasConsumed()) - - // out of gas during SudoError - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, error string) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - cachedCtx.GasMeter().ConsumeGas(7001, "out of gas test") - }) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 7000}) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Ack, &errACK) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = icak.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck) + err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(7000), ctx.GasMeter().GasConsumed()) - // success during SudoResponse + // error contract SudoResponse ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(ctx), contractAddress, p, resACK.GetResult()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldBeWrittenKey("sudoresponse"), ShouldBeWritten) // consumes 3140 gas, 2000 flat write + 30 every byte of key+value - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 8000}) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck).Return(nil, fmt.Errorf("error sudoResponse")) err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) - require.Equal(t, uint64(3140), ctx.GasMeter().GasConsumed()) - require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudoresponse"))) - - // not enough gas provided by relayer for SudoCallGasLimit - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - lowGasCtx := infCtx.WithGasMeter(sdk.NewGasMeter(1000)) - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(lowGasCtx), contractAddress, p, resACK.GetResult()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - cachedCtx.GasMeter().ConsumeGas(1001, "out of gas test") - }) - feeKeeper.EXPECT().DistributeAcknowledgementFee(lowGasCtx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().GetParams(lowGasCtx).Return(types.Params{SudoCallGasLimit: 9000}) - require.PanicsWithValue(t, sdk.ErrorOutOfGas{Descriptor: "consume gas from cached context"}, func() { icak.HandleAcknowledgement(lowGasCtx, p, resAckData, relayerAddress) }) //nolint:errcheck // this is a panic test } func TestHandleTimeout(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) - cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) + wmKeeper := mock_types.NewMockWasmKeeper(ctrl) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) - icak, infCtx, storeKey := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil) + bankKeeper := mock_types.NewMockBankKeeper(ctrl) + feeburnerKeeper := mock_types.NewMockFeeBurnerKeeper(ctrl) + icak, infCtx := testkeeper.InterchainTxsKeeper(t, wmKeeper, feeKeeper, icaKeeper, nil, bankKeeper, feeburnerKeeper) ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - store := ctx.KVStore(storeKey) contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) relayerBech32 := "neutron1fxudpred77a0grgh69u0j7y84yks5ev4n5050z45kecz792jnd6scqu98z" relayerAddress := sdk.MustAccAddressFromBech32(relayerBech32) @@ -164,73 +86,35 @@ func TestHandleTimeout(t *testing.T) { SourceChannel: "channel-0", } - err := icak.HandleTimeout(ctx, channeltypes.Packet{}, relayerAddress) - require.ErrorContains(t, err, "failed to get ica owner from port") - - gasReserved := false - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - if cachedCtx.GasMeter().Limit() == 4000 { - gasReserved = true - } - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldBeWrittenKey("sudotimeout"), ShouldBeWritten) // consumes 3110 gas, 2000 flat write + 30 every byte of key+value - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 4000}) - feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = icak.HandleTimeout(ctx, p, relayerAddress) - require.True(t, gasReserved) - require.Equal(t, uint64(3110), ctx.GasMeter().GasConsumed()) - require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudotimeout"))) + msgAck, err := keeper.PrepareSudoCallbackMessage(p, nil) require.NoError(t, err) - // error during SudoTimeOut + err = icak.HandleTimeout(ctx, channeltypes.Packet{}, relayerAddress) + require.ErrorContains(t, err, "failed to get ica owner from port") + + // contract success ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - }).Return(nil, fmt.Errorf("SudoTimeout error")) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 5000}) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Timeout, nil) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck) err = icak.HandleTimeout(ctx, p, relayerAddress) - require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - // out of gas during SudoTimeOut + // contract error ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - cachedCtx.GasMeter().ConsumeGas(6001, "out of gas test") - }).Return(nil, fmt.Errorf("SudoTimeout error")) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 6000}) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Timeout, nil) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck).Return(nil, fmt.Errorf("SudoTimeout error")) err = icak.HandleTimeout(ctx, p, relayerAddress) - require.Equal(t, uint64(6000), ctx.GasMeter().GasConsumed()) require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - // not enough gas provided by relayer for SudoCallGasLimit - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - lowGasCtx := infCtx.WithGasMeter(sdk.NewGasMeter(1000)) - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(lowGasCtx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - cachedCtx.GasMeter().ConsumeGas(1001, "out of gas test") - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(lowGasCtx).Return(types.Params{SudoCallGasLimit: 9000}) - feeKeeper.EXPECT().DistributeTimeoutFee(lowGasCtx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - require.PanicsWithValue(t, sdk.ErrorOutOfGas{Descriptor: "consume gas from cached context"}, func() { icak.HandleTimeout(lowGasCtx, p, relayerAddress) }) //nolint:errcheck // this is a panic test } func TestHandleChanOpenAck(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) - icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, nil, nil, nil) + wmKeeper := mock_types.NewMockWasmKeeper(ctrl) + bankKeeper := mock_types.NewMockBankKeeper(ctrl) + feeburnerKeeper := mock_types.NewMockFeeBurnerKeeper(ctrl) + icak, ctx := testkeeper.InterchainTxsKeeper(t, wmKeeper, nil, nil, nil, bankKeeper, feeburnerKeeper) portID := icatypes.ControllerPortPrefix + testutil.TestOwnerAddress + ".ica0" contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) channelID := "channel-0" @@ -239,21 +123,21 @@ func TestHandleChanOpenAck(t *testing.T) { err := icak.HandleChanOpenAck(ctx, "", channelID, counterpartyChannelID, "1") require.ErrorContains(t, err, "failed to get ica owner from port") - cmKeeper.EXPECT().SudoOnChanOpenAck(ctx, contractAddress, types.OpenAckDetails{ + msg, err := keeper.PrepareOpenAckCallbackMessage(types.OpenAckDetails{ PortID: portID, ChannelID: channelID, CounterpartyChannelID: counterpartyChannelID, CounterpartyVersion: "1", - }).Return(nil, fmt.Errorf("SudoOnChanOpenAck error")) + }) + require.NoError(t, err) + + // sudo error + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msg).Return(nil, fmt.Errorf("SudoOnChanOpenAck error")) err = icak.HandleChanOpenAck(ctx, portID, channelID, counterpartyChannelID, "1") - require.ErrorContains(t, err, "failed to Sudo the contract OnChanOpenAck") + require.NoError(t, err) - cmKeeper.EXPECT().SudoOnChanOpenAck(ctx, contractAddress, types.OpenAckDetails{ - PortID: portID, - ChannelID: channelID, - CounterpartyChannelID: counterpartyChannelID, - CounterpartyVersion: "1", - }).Return(nil, nil) + // sudo success + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msg) err = icak.HandleChanOpenAck(ctx, portID, channelID, counterpartyChannelID, "1") require.NoError(t, err) } diff --git a/x/interchaintxs/keeper/keeper.go b/x/interchaintxs/keeper/keeper.go index a1032c24c..0cf14c884 100644 --- a/x/interchaintxs/keeper/keeper.go +++ b/x/interchaintxs/keeper/keeper.go @@ -1,7 +1,9 @@ package keeper import ( + "cosmossdk.io/errors" "fmt" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" @@ -20,13 +22,15 @@ const ( type ( Keeper struct { - Codec codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey - channelKeeper types.ChannelKeeper - feeKeeper types.FeeRefunderKeeper - icaControllerKeeper types.ICAControllerKeeper - contractManagerKeeper types.ContractManagerKeeper + Codec codec.BinaryCodec + storeKey storetypes.StoreKey + memKey storetypes.StoreKey + channelKeeper types.ChannelKeeper + feeKeeper types.FeeRefunderKeeper + icaControllerKeeper types.ICAControllerKeeper + sudoKeeper types.WasmKeeper + bankKeeper types.BankKeeper + feeBurnerKeeper types.FeeBurnerKeeper } ) @@ -36,20 +40,46 @@ func NewKeeper( memKey storetypes.StoreKey, channelKeeper types.ChannelKeeper, icaControllerKeeper types.ICAControllerKeeper, - contractManagerKeeper types.ContractManagerKeeper, + sudoKeeper types.WasmKeeper, feeKeeper types.FeeRefunderKeeper, + bankKeeper types.BankKeeper, + feeBurnerKeeper types.FeeBurnerKeeper, ) *Keeper { return &Keeper{ - Codec: cdc, - storeKey: storeKey, - memKey: memKey, - channelKeeper: channelKeeper, - icaControllerKeeper: icaControllerKeeper, - contractManagerKeeper: contractManagerKeeper, - feeKeeper: feeKeeper, + Codec: cdc, + storeKey: storeKey, + memKey: memKey, + channelKeeper: channelKeeper, + icaControllerKeeper: icaControllerKeeper, + sudoKeeper: sudoKeeper, + feeKeeper: feeKeeper, + bankKeeper: bankKeeper, + feeBurnerKeeper: feeBurnerKeeper, } } func (k *Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } + +func (k Keeper) ChargeFee(ctx sdk.Context, payer sdk.AccAddress, fee sdk.Coins) error { + k.Logger(ctx).Debug("Trying to change fees", "payer", payer, "fee", fee) + + params := k.GetParams(ctx) + + if !fee.IsAnyGTE(params.RegisterFee) { + return errors.Wrapf(sdkerrors.ErrInsufficientFee, "provided fee is less than min governance set ack fee: %s < %s", fee, params.RegisterFee) + } + + treasury := k.feeBurnerKeeper.GetParams(ctx).TreasuryAddress + treasuryAddress, err := sdk.AccAddressFromBech32(treasury) + if err != nil { + return errors.Wrapf(sdkerrors.ErrInvalidAddress, "failed to convert treasury, bech32 to AccAddress: %s: %w", treasury, err) + } + + err = k.bankKeeper.SendCoins(ctx, payer, treasuryAddress, fee) + if err != nil { + return errors.Wrapf(err, "failed send fee(%s) from %s to %s", fee, payer, treasury) + } + return nil +} diff --git a/x/interchaintxs/keeper/msg_server.go b/x/interchaintxs/keeper/msg_server.go index 62d3cfe5a..4c23bfbef 100644 --- a/x/interchaintxs/keeper/msg_server.go +++ b/x/interchaintxs/keeper/msg_server.go @@ -43,11 +43,15 @@ func (k Keeper) RegisterInterchainAccount(goCtx context.Context, msg *ictxtypes. return nil, errors.Wrapf(sdkerrors.ErrInvalidAddress, "failed to parse address: %s", msg.FromAddress) } - if !k.contractManagerKeeper.HasContractInfo(ctx, senderAddr) { + if !k.sudoKeeper.HasContractInfo(ctx, senderAddr) { k.Logger(ctx).Debug("RegisterInterchainAccount: contract not found", "from_address", msg.FromAddress) return nil, errors.Wrapf(ictxtypes.ErrNotContract, "%s is not a contract address", msg.FromAddress) } + if err := k.ChargeFee(ctx, senderAddr, msg.RegisterFee); err != nil { + return nil, errors.Wrapf(err, "failed to charge fees to pay for RegisterInterchainAccount msg: %s", msg) + } + icaOwner := ictxtypes.NewICAOwnerFromAddress(senderAddr, msg.InterchainAccountId) // FIXME: empty version string doesn't look good @@ -79,7 +83,7 @@ func (k Keeper) SubmitTx(goCtx context.Context, msg *ictxtypes.MsgSubmitTx) (*ic return nil, errors.Wrapf(sdkerrors.ErrInvalidAddress, "failed to parse address: %s", msg.FromAddress) } - if !k.contractManagerKeeper.HasContractInfo(ctx, senderAddr) { + if !k.sudoKeeper.HasContractInfo(ctx, senderAddr) { k.Logger(ctx).Debug("SubmitTx: contract not found", "from_address", msg.FromAddress) return nil, errors.Wrapf(ictxtypes.ErrNotContract, "%s is not a contract address", msg.FromAddress) } diff --git a/x/interchaintxs/keeper/msg_server_test.go b/x/interchaintxs/keeper/msg_server_test.go index 3879525ac..635464474 100644 --- a/x/interchaintxs/keeper/msg_server_test.go +++ b/x/interchaintxs/keeper/msg_server_test.go @@ -2,6 +2,8 @@ package keeper_test import ( "fmt" + "github.com/neutron-org/neutron/app/params" + feeburnertypes "github.com/neutron-org/neutron/x/feeburner/types" "testing" "time" @@ -21,12 +23,16 @@ import ( "github.com/neutron-org/neutron/x/interchaintxs/types" ) +const TestTreasury = "neutron1dua3d89szsmd3vwg0y5a2689ah0g4x68ps8vew" + func TestRegisterInterchainAccount(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) - cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) - icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, nil, icaKeeper, nil) + wmKeeper := mock_types.NewMockWasmKeeper(ctrl) + bankKeeper := mock_types.NewMockBankKeeper(ctrl) + feeburnerKeeper := mock_types.NewMockFeeBurnerKeeper(ctrl) + icak, ctx := testkeeper.InterchainTxsKeeper(t, wmKeeper, nil, icaKeeper, nil, bankKeeper, feeburnerKeeper) goCtx := sdk.WrapSDKContext(ctx) msgRegAcc := types.MsgRegisterInterchainAccount{ @@ -41,18 +47,44 @@ func TestRegisterInterchainAccount(t *testing.T) { require.ErrorContains(t, err, "failed to parse address") require.Nil(t, resp) - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(false) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(false) resp, err = icak.RegisterInterchainAccount(goCtx, &msgRegAcc) require.ErrorContains(t, err, "is not a contract address") require.Nil(t, resp) - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + resp, err = icak.RegisterInterchainAccount(goCtx, &msgRegAcc) + require.ErrorContains(t, err, "failed to charge fees to pay for RegisterInterchainAccount msg") + require.Nil(t, resp) + + msgRegAcc.RegisterFee = sdk.NewCoins(sdk.NewCoin(params.DefaultDenom, sdk.NewInt(1000))) + + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + feeburnerKeeper.EXPECT().GetParams(ctx).Return(feeburnertypes.Params{ + TreasuryAddress: TestTreasury, + }) + bankKeeper.EXPECT().SendCoins(ctx, sdk.MustAccAddressFromBech32(msgRegAcc.FromAddress), sdk.MustAccAddressFromBech32(TestTreasury), msgRegAcc.RegisterFee) icaKeeper.EXPECT().RegisterInterchainAccount(ctx, msgRegAcc.ConnectionId, icaOwner.String(), "").Return(fmt.Errorf("failed to register ica")) resp, err = icak.RegisterInterchainAccount(goCtx, &msgRegAcc) require.ErrorContains(t, err, "failed to RegisterInterchainAccount") require.Nil(t, resp) - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + feeburnerKeeper.EXPECT().GetParams(ctx).Return(feeburnertypes.Params{ + TreasuryAddress: TestTreasury, + }) + bankKeeper.EXPECT(). + SendCoins(ctx, sdk.MustAccAddressFromBech32(msgRegAcc.FromAddress), sdk.MustAccAddressFromBech32(TestTreasury), msgRegAcc.RegisterFee). + Return(fmt.Errorf("failed to send coins")) + resp, err = icak.RegisterInterchainAccount(goCtx, &msgRegAcc) + require.ErrorContains(t, err, "failed to send coins") + require.Nil(t, resp) + + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + feeburnerKeeper.EXPECT().GetParams(ctx).Return(feeburnertypes.Params{ + TreasuryAddress: TestTreasury, + }) + bankKeeper.EXPECT().SendCoins(ctx, sdk.MustAccAddressFromBech32(msgRegAcc.FromAddress), sdk.MustAccAddressFromBech32(TestTreasury), msgRegAcc.RegisterFee) icaKeeper.EXPECT().RegisterInterchainAccount(ctx, msgRegAcc.ConnectionId, icaOwner.String(), "").Return(nil) resp, err = icak.RegisterInterchainAccount(goCtx, &msgRegAcc) require.NoError(t, err) @@ -63,10 +95,12 @@ func TestSubmitTx(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) - cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) + wmKeeper := mock_types.NewMockWasmKeeper(ctrl) refundKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) channelKeeper := mock_types.NewMockChannelKeeper(ctrl) - icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, refundKeeper, icaKeeper, channelKeeper) + bankKeeper := mock_types.NewMockBankKeeper(ctrl) + feeburnerKeeper := mock_types.NewMockFeeBurnerKeeper(ctrl) + icak, ctx := testkeeper.InterchainTxsKeeper(t, wmKeeper, refundKeeper, icaKeeper, channelKeeper, bankKeeper, feeburnerKeeper) goCtx := sdk.WrapSDKContext(ctx) cosmosMsg := codectypes.Any{ @@ -97,7 +131,7 @@ func TestSubmitTx(t *testing.T) { require.Nil(t, resp) require.ErrorContains(t, err, "failed to parse address") - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(false) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(false) resp, err = icak.SubmitTx(goCtx, &submitMsg) require.Nil(t, resp) require.ErrorContains(t, err, "is not a contract address") @@ -105,21 +139,21 @@ func TestSubmitTx(t *testing.T) { params := icak.GetParams(ctx) maxMsgs := params.GetMsgSubmitTxMaxMessages() submitMsg.Msgs = make([]*codectypes.Any, maxMsgs+1) - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) resp, err = icak.SubmitTx(goCtx, &submitMsg) require.Nil(t, resp) require.ErrorContains(t, err, "MsgSubmitTx contains more messages than allowed") submitMsg.Msgs = []*codectypes.Any{&cosmosMsg} portID := "icacontroller-" + testutil.TestOwnerAddress + ".ica0" - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) icaKeeper.EXPECT().GetActiveChannelID(ctx, "connection-0", portID).Return("", false) resp, err = icak.SubmitTx(goCtx, &submitMsg) require.Nil(t, resp) require.ErrorContains(t, err, "failed to GetActiveChannelID for port") activeChannel := "channel-0" - // cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + // wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) // icaKeeper.EXPECT().GetActiveChannelID(ctx, "connection-0", portID).Return(activeChannel, true) // currCodec := icak.Codec // icak.Codec = &codec.AminoCodec{} @@ -128,7 +162,7 @@ func TestSubmitTx(t *testing.T) { // require.Nil(t, resp) // require.ErrorContains(t, err, "only ProtoCodec is supported for receiving messages on the host chain") - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) icaKeeper.EXPECT().GetActiveChannelID(ctx, "connection-0", portID).Return(activeChannel, true) channelKeeper.EXPECT().GetNextSequenceSend(ctx, portID, activeChannel).Return(uint64(0), false) resp, err = icak.SubmitTx(goCtx, &submitMsg) @@ -136,7 +170,7 @@ func TestSubmitTx(t *testing.T) { require.ErrorContains(t, err, "sequence send not found") sequence := uint64(100) - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) icaKeeper.EXPECT().GetActiveChannelID(ctx, "connection-0", portID).Return(activeChannel, true) channelKeeper.EXPECT().GetNextSequenceSend(ctx, portID, activeChannel).Return(sequence, true) refundKeeper.EXPECT().LockFees(ctx, contractAddress, feerefundertypes.NewPacketID(portID, activeChannel, sequence), submitMsg.Fee).Return(fmt.Errorf("failed to lock fees")) @@ -153,7 +187,7 @@ func TestSubmitTx(t *testing.T) { } timeoutTimestamp := ctx.BlockTime().Add(time.Duration(submitMsg.Timeout) * time.Second).UnixNano() - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) icaKeeper.EXPECT().GetActiveChannelID(ctx, "connection-0", portID).Return(activeChannel, true) channelKeeper.EXPECT().GetNextSequenceSend(ctx, portID, activeChannel).Return(sequence, true) refundKeeper.EXPECT().LockFees(ctx, contractAddress, feerefundertypes.NewPacketID(portID, activeChannel, sequence), submitMsg.Fee).Return(nil) @@ -162,7 +196,7 @@ func TestSubmitTx(t *testing.T) { require.Nil(t, resp) require.ErrorContains(t, err, "failed to SendTx") - cmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, contractAddress).Return(true) icaKeeper.EXPECT().GetActiveChannelID(ctx, "connection-0", portID).Return(activeChannel, true) channelKeeper.EXPECT().GetNextSequenceSend(ctx, portID, activeChannel).Return(sequence, true) refundKeeper.EXPECT().LockFees(ctx, contractAddress, feerefundertypes.NewPacketID(portID, activeChannel, sequence), submitMsg.Fee).Return(nil) diff --git a/x/interchaintxs/keeper/params_test.go b/x/interchaintxs/keeper/params_test.go index 0ecf910fc..0899ac765 100644 --- a/x/interchaintxs/keeper/params_test.go +++ b/x/interchaintxs/keeper/params_test.go @@ -10,7 +10,7 @@ import ( ) func TestGetParams(t *testing.T) { - k, ctx, _ := testkeeper.InterchainTxsKeeper(t, nil, nil, nil, nil) + k, ctx := testkeeper.InterchainTxsKeeper(t, nil, nil, nil, nil, nil, nil) params := types.DefaultParams() err := k.SetParams(ctx, params) diff --git a/x/interchaintxs/types/expected_keepers.go b/x/interchaintxs/types/expected_keepers.go index a9be5877b..23648d88d 100644 --- a/x/interchaintxs/types/expected_keepers.go +++ b/x/interchaintxs/types/expected_keepers.go @@ -7,8 +7,8 @@ import ( icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + feeburnertypes "github.com/neutron-org/neutron/x/feeburner/types" - contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" feerefundertypes "github.com/neutron-org/neutron/x/feerefunder/types" ) @@ -21,17 +21,12 @@ type AccountKeeper interface { // BankKeeper defines the expected interface needed to retrieve account balances. type BankKeeper interface { SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - // Methods imported from bank should be defined here + SendCoins(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error } -type ContractManagerKeeper interface { +type WasmKeeper interface { HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool - AddContractFailure(ctx sdk.Context, packet *channeltypes.Packet, address, ackType string, ack *channeltypes.Acknowledgement) - SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) ([]byte, error) - SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, details string) ([]byte, error) - SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) - SudoOnChanOpenAck(ctx sdk.Context, contractAddress sdk.AccAddress, details contractmanagertypes.OpenAckDetails) ([]byte, error) - GetParams(ctx sdk.Context) (params contractmanagertypes.Params) + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) } type ICAControllerKeeper interface { @@ -53,3 +48,7 @@ type ChannelKeeper interface { GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) GetConnection(ctx sdk.Context, connectionID string) (ibcexported.ConnectionI, error) } + +type FeeBurnerKeeper interface { + GetParams(ctx sdk.Context) feeburnertypes.Params +} diff --git a/x/interchaintxs/types/params.go b/x/interchaintxs/types/params.go index c9216156e..7a9f54638 100644 --- a/x/interchaintxs/types/params.go +++ b/x/interchaintxs/types/params.go @@ -2,8 +2,10 @@ package types import ( "fmt" - + sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/neutron-org/neutron/app/params" + "gopkg.in/yaml.v2" ) @@ -12,29 +14,20 @@ var _ paramtypes.ParamSet = (*Params)(nil) var ( KeyMsgSubmitTxMaxMessages = []byte("MsgSubmitTxMaxMessages") DefaultMsgSubmitTxMaxMessages = uint64(16) + DefaultRegisterFee = sdk.NewCoins(sdk.NewCoin(params.DefaultDenom, sdk.NewInt(1000))) ) -// ParamKeyTable the param key table for launch module -func ParamKeyTable() paramtypes.KeyTable { - return paramtypes.NewKeyTable( - paramtypes.NewParamSetPair( - KeyMsgSubmitTxMaxMessages, - DefaultMsgSubmitTxMaxMessages, - validateMsgSubmitTxMaxMessages, - ), - ) -} - // NewParams creates a new Params instance -func NewParams(msgSubmitTxMaxMessages uint64) Params { +func NewParams(msgSubmitTxMaxMessages uint64, registerFee sdk.Coins) Params { return Params{ MsgSubmitTxMaxMessages: msgSubmitTxMaxMessages, + RegisterFee: registerFee, } } // DefaultParams returns a default set of parameters func DefaultParams() Params { - return NewParams(DefaultMsgSubmitTxMaxMessages) + return NewParams(DefaultMsgSubmitTxMaxMessages, DefaultRegisterFee) } // ParamSetPairs get the params.ParamSet diff --git a/x/interchaintxs/types/params.pb.go b/x/interchaintxs/types/params.pb.go index e43702998..00f396fe7 100644 --- a/x/interchaintxs/types/params.pb.go +++ b/x/interchaintxs/types/params.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" @@ -27,6 +28,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Params struct { // Defines maximum amount of messages to be passed in MsgSubmitTx MsgSubmitTxMaxMessages uint64 `protobuf:"varint,1,opt,name=msg_submit_tx_max_messages,json=msgSubmitTxMaxMessages,proto3" json:"msg_submit_tx_max_messages,omitempty"` + // Defines a minimum fee required to register interchain account + RegisterFee []types.Coin `protobuf:"bytes,2,rep,name=register_fee,json=registerFee,proto3" json:"register_fee"` } func (m *Params) Reset() { *m = Params{} } @@ -68,6 +71,13 @@ func (m *Params) GetMsgSubmitTxMaxMessages() uint64 { return 0 } +func (m *Params) GetRegisterFee() []types.Coin { + if m != nil { + return m.RegisterFee + } + return nil +} + func init() { proto.RegisterType((*Params)(nil), "neutron.interchaintxs.Params") } @@ -77,21 +87,25 @@ func init() { } var fileDescriptor_52b0ced89d3fa9c6 = []byte{ - // 211 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcd, 0x4b, 0x2d, 0x2d, - 0x29, 0xca, 0xcf, 0xd3, 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0x4a, 0xce, 0x48, 0xcc, 0xcc, 0x2b, 0xa9, - 0x28, 0xd6, 0x2f, 0x33, 0xd4, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, - 0xc9, 0x17, 0x12, 0x85, 0x2a, 0xd3, 0x43, 0x51, 0x26, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, - 0xa1, 0x0f, 0x62, 0x41, 0x14, 0x2b, 0x79, 0x71, 0xb1, 0x05, 0x80, 0x35, 0x0b, 0x59, 0x71, 0x49, - 0xe5, 0x16, 0xa7, 0xc7, 0x17, 0x97, 0x26, 0xe5, 0x66, 0x96, 0xc4, 0x97, 0x54, 0xc4, 0xe7, 0x26, - 0x56, 0xc4, 0xe7, 0xa6, 0x16, 0x17, 0x27, 0xa6, 0xa7, 0x16, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0xb0, - 0x04, 0x89, 0xe5, 0x16, 0xa7, 0x07, 0x83, 0x15, 0x84, 0x54, 0xf8, 0x26, 0x56, 0xf8, 0x42, 0x65, - 0xad, 0x58, 0x66, 0x2c, 0x90, 0x67, 0x70, 0xf2, 0x3b, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, - 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, - 0x39, 0x86, 0x28, 0x93, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, - 0xeb, 0x74, 0xf3, 0x8b, 0xd2, 0x61, 0x6c, 0xfd, 0x0a, 0x34, 0x2f, 0x95, 0x54, 0x16, 0xa4, 0x16, - 0x27, 0xb1, 0x81, 0x9d, 0x68, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x61, 0xb1, 0xdd, 0xe6, 0xf8, - 0x00, 0x00, 0x00, + // 285 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x90, 0x31, 0x4b, 0x03, 0x31, + 0x18, 0x86, 0x2f, 0x5a, 0x3a, 0x5c, 0x9d, 0x8a, 0x4a, 0xed, 0x90, 0x16, 0x41, 0xe8, 0x62, 0x42, + 0xd5, 0xa9, 0x63, 0x05, 0xb7, 0x8a, 0x54, 0x27, 0x97, 0x23, 0x77, 0x7c, 0xa6, 0x19, 0x92, 0x1c, + 0xf9, 0x72, 0x25, 0xfe, 0x09, 0x71, 0x74, 0xf4, 0xe7, 0x74, 0xec, 0xe8, 0x24, 0x72, 0xf7, 0x47, + 0xa4, 0x77, 0xd7, 0x41, 0xb7, 0x17, 0xde, 0x27, 0x79, 0xf8, 0xde, 0xf8, 0xc2, 0x40, 0xe1, 0x9d, + 0x35, 0x5c, 0x19, 0x0f, 0x2e, 0x5b, 0x09, 0x65, 0x7c, 0x40, 0xbe, 0x9e, 0xf2, 0x5c, 0x38, 0xa1, + 0x91, 0xe5, 0xce, 0x7a, 0xdb, 0x3f, 0x69, 0x31, 0xf6, 0x07, 0x1b, 0x1e, 0x4b, 0x2b, 0x6d, 0x4d, + 0xf0, 0x5d, 0x6a, 0xe0, 0x21, 0xcd, 0x2c, 0x6a, 0x8b, 0x3c, 0x15, 0x08, 0x7c, 0x3d, 0x4d, 0xc1, + 0x8b, 0x29, 0xcf, 0xac, 0x32, 0x4d, 0x7f, 0xfe, 0x46, 0xe2, 0xee, 0x43, 0xfd, 0x7b, 0x7f, 0x16, + 0x0f, 0x35, 0xca, 0x04, 0x8b, 0x54, 0x2b, 0x9f, 0xf8, 0x90, 0x68, 0x11, 0x12, 0x0d, 0x88, 0x42, + 0x02, 0x0e, 0xc8, 0x98, 0x4c, 0x3a, 0xcb, 0x53, 0x8d, 0xf2, 0xb1, 0x06, 0x9e, 0xc2, 0x42, 0x84, + 0x45, 0xdb, 0xf6, 0xe7, 0xf1, 0x91, 0x03, 0xa9, 0xd0, 0x83, 0x4b, 0x5e, 0x00, 0x06, 0x07, 0xe3, + 0xc3, 0x49, 0xef, 0xea, 0x8c, 0x35, 0x76, 0xb6, 0xb3, 0xb3, 0xd6, 0xce, 0x6e, 0xad, 0x32, 0xf3, + 0xce, 0xe6, 0x7b, 0x14, 0x2d, 0x7b, 0xfb, 0x47, 0x77, 0x00, 0xb3, 0xce, 0xc7, 0xe7, 0x28, 0x9a, + 0xdf, 0x6f, 0x4a, 0x4a, 0xb6, 0x25, 0x25, 0x3f, 0x25, 0x25, 0xef, 0x15, 0x8d, 0xb6, 0x15, 0x8d, + 0xbe, 0x2a, 0x1a, 0x3d, 0xdf, 0x48, 0xe5, 0x57, 0x45, 0xca, 0x32, 0xab, 0x79, 0x3b, 0xc1, 0xa5, + 0x75, 0x72, 0x9f, 0x79, 0xf8, 0xb7, 0x9b, 0x7f, 0xcd, 0x01, 0xd3, 0x6e, 0x7d, 0xe7, 0xf5, 0x6f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x81, 0x7b, 0xd6, 0x7d, 0x5d, 0x01, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -114,6 +128,20 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.RegisterFee) > 0 { + for iNdEx := len(m.RegisterFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RegisterFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } if m.MsgSubmitTxMaxMessages != 0 { i = encodeVarintParams(dAtA, i, uint64(m.MsgSubmitTxMaxMessages)) i-- @@ -142,6 +170,12 @@ func (m *Params) Size() (n int) { if m.MsgSubmitTxMaxMessages != 0 { n += 1 + sovParams(uint64(m.MsgSubmitTxMaxMessages)) } + if len(m.RegisterFee) > 0 { + for _, e := range m.RegisterFee { + l = e.Size() + n += 1 + l + sovParams(uint64(l)) + } + } return n } @@ -199,6 +233,40 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RegisterFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RegisterFee = append(m.RegisterFee, types.Coin{}) + if err := m.RegisterFee[len(m.RegisterFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) diff --git a/x/interchaintxs/types/tx.pb.go b/x/interchaintxs/types/tx.pb.go index 41bf28b30..80aa5ff45 100644 --- a/x/interchaintxs/types/tx.pb.go +++ b/x/interchaintxs/types/tx.pb.go @@ -7,11 +7,13 @@ import ( context "context" fmt "fmt" _ "github.com/cosmos/cosmos-proto" - types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" - types1 "github.com/neutron-org/neutron/x/feerefunder/types" + types2 "github.com/neutron-org/neutron/x/feerefunder/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -34,9 +36,10 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // MsgRegisterInterchainAccount is used to register an account on a remote zone. type MsgRegisterInterchainAccount struct { - FromAddress string `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` - ConnectionId string `protobuf:"bytes,2,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` - InterchainAccountId string `protobuf:"bytes,3,opt,name=interchain_account_id,json=interchainAccountId,proto3" json:"interchain_account_id,omitempty" yaml:"interchain_account_id"` + FromAddress string `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` + ConnectionId string `protobuf:"bytes,2,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` + InterchainAccountId string `protobuf:"bytes,3,opt,name=interchain_account_id,json=interchainAccountId,proto3" json:"interchain_account_id,omitempty" yaml:"interchain_account_id"` + RegisterFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=register_fee,json=registerFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"register_fee"` } func (m *MsgRegisterInterchainAccount) Reset() { *m = MsgRegisterInterchainAccount{} } @@ -117,13 +120,13 @@ type MsgSubmitTx struct { // lido/kava. This allows contracts to have more than one interchain accounts // on remote zone This identifier will be a part of the portID that we'll // claim our capability for. - InterchainAccountId string `protobuf:"bytes,2,opt,name=interchain_account_id,json=interchainAccountId,proto3" json:"interchain_account_id,omitempty"` - ConnectionId string `protobuf:"bytes,3,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` - Msgs []*types.Any `protobuf:"bytes,4,rep,name=msgs,proto3" json:"msgs,omitempty"` - Memo string `protobuf:"bytes,5,opt,name=memo,proto3" json:"memo,omitempty"` + InterchainAccountId string `protobuf:"bytes,2,opt,name=interchain_account_id,json=interchainAccountId,proto3" json:"interchain_account_id,omitempty"` + ConnectionId string `protobuf:"bytes,3,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` + Msgs []*types1.Any `protobuf:"bytes,4,rep,name=msgs,proto3" json:"msgs,omitempty"` + Memo string `protobuf:"bytes,5,opt,name=memo,proto3" json:"memo,omitempty"` // timeout in seconds after which the packet times out Timeout uint64 `protobuf:"varint,6,opt,name=timeout,proto3" json:"timeout,omitempty"` - Fee types1.Fee `protobuf:"bytes,7,opt,name=fee,proto3" json:"fee"` + Fee types2.Fee `protobuf:"bytes,7,opt,name=fee,proto3" json:"fee"` } func (m *MsgSubmitTx) Reset() { *m = MsgSubmitTx{} } @@ -224,43 +227,48 @@ func init() { func init() { proto.RegisterFile("neutron/interchaintxs/v1/tx.proto", fileDescriptor_50f087790e59c806) } var fileDescriptor_50f087790e59c806 = []byte{ - // 576 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xbd, 0x6f, 0xda, 0x4e, - 0x18, 0xb6, 0x03, 0xbf, 0x24, 0xbf, 0x23, 0x5d, 0x2e, 0x89, 0x64, 0x10, 0xb5, 0x89, 0xfb, 0x21, - 0x96, 0x9c, 0x1b, 0x5a, 0x75, 0x88, 0xd4, 0x4a, 0x30, 0x54, 0x62, 0xa0, 0xaa, 0xdc, 0x4c, 0x5d, - 0x90, 0xb1, 0x5f, 0x0e, 0x4b, 0xf8, 0x8e, 0xfa, 0xce, 0x11, 0x8c, 0xdd, 0x3a, 0x76, 0xe9, 0x58, - 0x29, 0x7f, 0x4e, 0xc6, 0x8c, 0x9d, 0x50, 0x04, 0x4b, 0xe7, 0xfc, 0x05, 0x95, 0xbf, 0x80, 0xa0, - 0x80, 0xa2, 0x6e, 0xef, 0xc7, 0x73, 0xcf, 0x3d, 0xef, 0x73, 0xaf, 0x8d, 0x4e, 0x18, 0x44, 0x32, - 0xe4, 0xcc, 0xf2, 0x99, 0x84, 0xd0, 0x1d, 0x38, 0x3e, 0x93, 0x63, 0x61, 0x5d, 0x9e, 0x59, 0x72, - 0x4c, 0x46, 0x21, 0x97, 0x1c, 0x6b, 0x19, 0x84, 0xdc, 0x83, 0x90, 0xcb, 0xb3, 0x4a, 0xd9, 0xe5, - 0x22, 0xe0, 0xa2, 0x9b, 0xe0, 0xac, 0x34, 0x49, 0x0f, 0x55, 0x8e, 0x28, 0xa7, 0x3c, 0xad, 0xc7, - 0x51, 0x56, 0x3d, 0xa6, 0x9c, 0xd3, 0x21, 0x58, 0xce, 0xc8, 0xb7, 0x06, 0x52, 0x8e, 0xb2, 0x72, - 0x75, 0xa5, 0xec, 0x30, 0xc6, 0xa5, 0x23, 0x7d, 0xce, 0x72, 0xaa, 0x72, 0xd6, 0x4d, 0xb2, 0x5e, - 0xd4, 0xb7, 0x1c, 0x36, 0xc9, 0x5a, 0x4f, 0x73, 0xf5, 0x7d, 0x80, 0x10, 0xfa, 0x11, 0xf3, 0x20, - 0x8c, 0xe3, 0xb4, 0x6d, 0xde, 0xaa, 0xa8, 0xda, 0x11, 0xd4, 0x06, 0xea, 0x0b, 0x09, 0x61, 0x7b, - 0xa1, 0xbf, 0xe9, 0xba, 0x3c, 0x62, 0x12, 0x9f, 0xa0, 0x83, 0x7e, 0xc8, 0x83, 0xae, 0xe3, 0x79, - 0x21, 0x08, 0xa1, 0xa9, 0x35, 0xb5, 0xfe, 0xbf, 0x5d, 0x8a, 0x6b, 0xcd, 0xb4, 0x84, 0xdf, 0xa1, - 0x27, 0x2e, 0x67, 0x0c, 0xdc, 0x58, 0x52, 0xd7, 0xf7, 0xb4, 0x9d, 0x18, 0xd3, 0xd2, 0xee, 0xa6, - 0xc6, 0xd1, 0xc4, 0x09, 0x86, 0xe7, 0xe6, 0xbd, 0xb6, 0x69, 0x1f, 0x2c, 0xf3, 0xb6, 0x87, 0x2f, - 0xd0, 0xf1, 0xd2, 0xb6, 0xae, 0x93, 0xde, 0x1b, 0xd3, 0x14, 0x12, 0x9a, 0xda, 0xdd, 0xd4, 0xa8, - 0xa6, 0x34, 0x0f, 0xc2, 0x4c, 0xfb, 0xd0, 0x5f, 0x57, 0xdd, 0xf6, 0xce, 0xf7, 0xbf, 0x5f, 0x19, - 0xca, 0x9f, 0x2b, 0x43, 0x31, 0x5f, 0xa2, 0xe7, 0xdb, 0x26, 0xb4, 0x41, 0x8c, 0x38, 0x13, 0x60, - 0xfe, 0xda, 0x41, 0xa5, 0x8e, 0xa0, 0x9f, 0xa3, 0x5e, 0xe0, 0xcb, 0x8b, 0xf1, 0x63, 0x26, 0x6f, - 0x6c, 0x92, 0x9e, 0x38, 0xf0, 0xa0, 0x30, 0xfc, 0x6c, 0xdd, 0xad, 0x64, 0xcc, 0x35, 0x4f, 0xea, - 0xa8, 0x18, 0x08, 0x2a, 0xb4, 0x62, 0xad, 0x50, 0x2f, 0x35, 0x8e, 0x48, 0xfa, 0xbe, 0x24, 0x7f, - 0x5f, 0xd2, 0x64, 0x13, 0x3b, 0x41, 0x60, 0x8c, 0x8a, 0x01, 0x04, 0x5c, 0xfb, 0x2f, 0x61, 0x49, - 0x62, 0xac, 0xa1, 0x3d, 0xe9, 0x07, 0xc0, 0x23, 0xa9, 0xed, 0xd6, 0xd4, 0x7a, 0xd1, 0xce, 0x53, - 0xfc, 0x0a, 0x15, 0xfa, 0x00, 0xda, 0x5e, 0x4d, 0xad, 0x97, 0x1a, 0x1a, 0xc9, 0xd7, 0x76, 0x65, - 0x37, 0xc8, 0x07, 0x80, 0x56, 0xf1, 0x7a, 0x6a, 0x28, 0x76, 0x0c, 0x5d, 0xf1, 0xf1, 0x13, 0x3a, - 0x5c, 0xb1, 0x27, 0xb7, 0x0d, 0x1b, 0xa8, 0x24, 0xe0, 0x6b, 0x04, 0xcc, 0x85, 0x78, 0x1a, 0x35, - 0xb9, 0x10, 0xe5, 0xa5, 0xb6, 0x17, 0xab, 0x71, 0x07, 0x0e, 0x63, 0x30, 0xcc, 0x6c, 0xc9, 0xd3, - 0xc6, 0xb7, 0x1d, 0x54, 0xe8, 0x08, 0x8a, 0x7f, 0xaa, 0xa8, 0xbc, 0x79, 0x03, 0xdf, 0x92, 0x4d, - 0x5f, 0x17, 0xd9, 0xf6, 0xae, 0x95, 0xf7, 0xff, 0x76, 0x6e, 0xb1, 0x0f, 0x0a, 0xee, 0xa1, 0xfd, - 0xc5, 0x36, 0xbc, 0xd8, 0xca, 0x96, 0xc3, 0x2a, 0xa7, 0x8f, 0x82, 0x2d, 0xef, 0x68, 0x7d, 0xbc, - 0x9e, 0xe9, 0xea, 0xcd, 0x4c, 0x57, 0x6f, 0x67, 0xba, 0xfa, 0x63, 0xae, 0x2b, 0x37, 0x73, 0x5d, - 0xf9, 0x3d, 0xd7, 0x95, 0x2f, 0x6f, 0xa8, 0x2f, 0x07, 0x51, 0x8f, 0xb8, 0x3c, 0xb0, 0x32, 0xd2, - 0x53, 0x1e, 0xd2, 0x3c, 0xb6, 0xc6, 0x6b, 0x3f, 0x24, 0x39, 0x19, 0x81, 0xe8, 0xed, 0x26, 0x3b, - 0xf2, 0xfa, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x30, 0x09, 0xc8, 0x02, 0xb6, 0x04, 0x00, 0x00, + // 645 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xbf, 0x4f, 0xdb, 0x40, + 0x14, 0xb6, 0x93, 0x14, 0xe8, 0x85, 0x2e, 0x06, 0x24, 0x27, 0xa2, 0x76, 0x48, 0x7f, 0xc8, 0x0b, + 0x67, 0x92, 0x56, 0x1d, 0x90, 0x5a, 0x89, 0x54, 0x42, 0xca, 0x40, 0x55, 0xb9, 0x4c, 0x5d, 0x22, + 0xff, 0x78, 0x71, 0xac, 0xe2, 0xbb, 0xd4, 0x77, 0x46, 0xc9, 0xd8, 0xad, 0x63, 0x97, 0x8e, 0x95, + 0x98, 0xfb, 0x97, 0x30, 0x32, 0x76, 0xa2, 0x15, 0x2c, 0x9d, 0xf9, 0x0b, 0xaa, 0x3b, 0x9f, 0x21, + 0x44, 0x80, 0x50, 0x27, 0xdf, 0x7b, 0xef, 0x7b, 0xef, 0xdd, 0xfb, 0xde, 0x77, 0x46, 0x1b, 0x04, + 0x72, 0x9e, 0x51, 0xe2, 0x26, 0x84, 0x43, 0x16, 0x8e, 0xfc, 0x84, 0xf0, 0x09, 0x73, 0x0f, 0x3b, + 0x2e, 0x9f, 0xe0, 0x71, 0x46, 0x39, 0x35, 0x4c, 0x05, 0xc1, 0xd7, 0x20, 0xf8, 0xb0, 0xd3, 0x6c, + 0x84, 0x94, 0xa5, 0x94, 0x0d, 0x24, 0xce, 0x2d, 0x8c, 0x22, 0xa9, 0x69, 0x15, 0x96, 0x1b, 0xf8, + 0x0c, 0xdc, 0xc3, 0x4e, 0x00, 0xdc, 0xef, 0xb8, 0x21, 0x4d, 0x88, 0x8a, 0xaf, 0xc6, 0x34, 0xa6, + 0x45, 0x9e, 0x38, 0x29, 0xef, 0x5a, 0x4c, 0x69, 0x7c, 0x00, 0xae, 0x3f, 0x4e, 0xdc, 0x11, 0xe7, + 0x63, 0xe5, 0x5e, 0x9f, 0x71, 0xfb, 0x84, 0x50, 0xee, 0xf3, 0x84, 0x92, 0xb2, 0x55, 0x43, 0x45, + 0xa5, 0x15, 0xe4, 0x43, 0xd7, 0x27, 0x53, 0x15, 0x7a, 0x5c, 0x4e, 0x37, 0x04, 0xc8, 0x60, 0x98, + 0x93, 0x08, 0x32, 0x71, 0x2e, 0xc2, 0xed, 0x93, 0x0a, 0x5a, 0xdf, 0x63, 0xb1, 0x07, 0x71, 0xc2, + 0x38, 0x64, 0xfd, 0xcb, 0xf9, 0x76, 0xc2, 0x90, 0xe6, 0x84, 0x1b, 0x1b, 0x68, 0x79, 0x98, 0xd1, + 0x74, 0xe0, 0x47, 0x51, 0x06, 0x8c, 0x99, 0x7a, 0x4b, 0x77, 0x1e, 0x7a, 0x75, 0xe1, 0xdb, 0x29, + 0x5c, 0xc6, 0x6b, 0xf4, 0x28, 0xa4, 0x84, 0x40, 0x28, 0xae, 0x34, 0x48, 0x22, 0xb3, 0x22, 0x30, + 0x3d, 0xf3, 0xe2, 0xd4, 0x5e, 0x9d, 0xfa, 0xe9, 0xc1, 0x76, 0xfb, 0x5a, 0xb8, 0xed, 0x2d, 0x5f, + 0xd9, 0xfd, 0xc8, 0xd8, 0x47, 0x6b, 0x57, 0xb4, 0x0e, 0xfc, 0xa2, 0xaf, 0x28, 0x53, 0x95, 0x65, + 0x5a, 0x17, 0xa7, 0xf6, 0x7a, 0x51, 0xe6, 0x46, 0x58, 0xdb, 0x5b, 0x49, 0xe6, 0x6f, 0xdd, 0x8f, + 0x0c, 0x82, 0x96, 0x33, 0x35, 0xd4, 0x60, 0x08, 0x60, 0xd6, 0x5a, 0x55, 0xa7, 0xde, 0x6d, 0x60, + 0xb5, 0x22, 0xb1, 0x14, 0xac, 0x96, 0x82, 0xdf, 0xd2, 0x84, 0xf4, 0xb6, 0x8e, 0x4f, 0x6d, 0xed, + 0xe7, 0x6f, 0xdb, 0x89, 0x13, 0x3e, 0xca, 0x03, 0x1c, 0xd2, 0x54, 0xed, 0x53, 0x7d, 0x36, 0x59, + 0xf4, 0xc9, 0xe5, 0xd3, 0x31, 0x30, 0x99, 0xc0, 0xbc, 0x7a, 0xd9, 0x60, 0x17, 0x60, 0x7b, 0xe9, + 0xeb, 0x91, 0xad, 0xfd, 0x3d, 0xb2, 0xb5, 0xf6, 0x73, 0xf4, 0xf4, 0x2e, 0x46, 0x3d, 0x60, 0x63, + 0x4a, 0x18, 0xb4, 0x7f, 0x54, 0x50, 0x7d, 0x8f, 0xc5, 0x1f, 0xf2, 0x20, 0x4d, 0xf8, 0xfe, 0xe4, + 0x3e, 0x4c, 0x77, 0x6f, 0xa3, 0x4a, 0x32, 0x7e, 0x33, 0x11, 0x4f, 0xe6, 0xb7, 0x23, 0x69, 0x9d, + 0xdb, 0x81, 0x83, 0x6a, 0x29, 0x8b, 0x99, 0x62, 0x69, 0x15, 0x17, 0x7a, 0xc2, 0xa5, 0x9e, 0xf0, + 0x0e, 0x99, 0x7a, 0x12, 0x61, 0x18, 0xa8, 0x96, 0x42, 0x4a, 0xcd, 0x07, 0xb2, 0x8a, 0x3c, 0x1b, + 0x26, 0x5a, 0xe4, 0x49, 0x0a, 0x34, 0xe7, 0xe6, 0x42, 0x4b, 0x77, 0x6a, 0x5e, 0x69, 0x1a, 0x5b, + 0xa8, 0x2a, 0xc8, 0x5f, 0x6c, 0xe9, 0x4e, 0xbd, 0x6b, 0xe2, 0xf2, 0x19, 0xcd, 0x68, 0x11, 0xef, + 0x02, 0xf4, 0x6a, 0x82, 0x7b, 0x4f, 0x40, 0x67, 0x78, 0x7c, 0x8f, 0x56, 0x66, 0xe8, 0x29, 0x69, + 0x33, 0x6c, 0x54, 0x67, 0xf0, 0x39, 0x07, 0x12, 0x82, 0x98, 0x46, 0x97, 0x0d, 0x51, 0xe9, 0xea, + 0x47, 0xe2, 0x36, 0xe1, 0xc8, 0x27, 0x04, 0x0e, 0x14, 0x2d, 0xa5, 0xd9, 0xfd, 0x52, 0x41, 0xd5, + 0x3d, 0x16, 0x1b, 0xdf, 0x75, 0xd4, 0xb8, 0x5d, 0xf1, 0xaf, 0xf0, 0x6d, 0xaf, 0x1d, 0xdf, 0xb5, + 0xd7, 0xe6, 0x9b, 0xff, 0xcb, 0xbb, 0xd4, 0x83, 0x66, 0x04, 0x68, 0xe9, 0x52, 0x0d, 0xcf, 0xee, + 0xac, 0x56, 0xc2, 0x9a, 0x9b, 0xf7, 0x82, 0x5d, 0xf5, 0xe8, 0xbd, 0x3b, 0x3e, 0xb3, 0xf4, 0x93, + 0x33, 0x4b, 0xff, 0x73, 0x66, 0xe9, 0xdf, 0xce, 0x2d, 0xed, 0xe4, 0xdc, 0xd2, 0x7e, 0x9d, 0x5b, + 0xda, 0xc7, 0x97, 0x33, 0xc2, 0x57, 0x45, 0x37, 0x69, 0x16, 0x97, 0x67, 0x77, 0x32, 0xf7, 0x83, + 0x94, 0x4f, 0x21, 0x58, 0x90, 0x1a, 0x79, 0xf1, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x6a, 0x12, 0xe8, + 0x47, 0x46, 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -399,6 +407,20 @@ func (m *MsgRegisterInterchainAccount) MarshalToSizedBuffer(dAtA []byte) (int, e _ = i var l int _ = l + if len(m.RegisterFee) > 0 { + for iNdEx := len(m.RegisterFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RegisterFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } if len(m.InterchainAccountId) > 0 { i -= len(m.InterchainAccountId) copy(dAtA[i:], m.InterchainAccountId) @@ -590,6 +612,12 @@ func (m *MsgRegisterInterchainAccount) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + if len(m.RegisterFee) > 0 { + for _, e := range m.RegisterFee { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } return n } @@ -785,6 +813,40 @@ func (m *MsgRegisterInterchainAccount) Unmarshal(dAtA []byte) error { } m.InterchainAccountId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RegisterFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RegisterFee = append(m.RegisterFee, types.Coin{}) + if err := m.RegisterFee[len(m.RegisterFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -1010,7 +1072,7 @@ func (m *MsgSubmitTx) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Msgs = append(m.Msgs, &types.Any{}) + m.Msgs = append(m.Msgs, &types1.Any{}) if err := m.Msgs[len(m.Msgs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } diff --git a/x/transfer/ibc_handlers.go b/x/transfer/ibc_handlers.go index a3a7b72ed..30ad94af3 100644 --- a/x/transfer/ibc_handlers.go +++ b/x/transfer/ibc_handlers.go @@ -1,21 +1,17 @@ package transfer import ( - contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" - "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - "github.com/neutron-org/neutron/neutronutils" - - neutronerrors "github.com/neutron-org/neutron/neutronutils/errors" + "github.com/neutron-org/neutron/x/contractmanager/keeper" feetypes "github.com/neutron-org/neutron/x/feerefunder/types" "github.com/neutron-org/neutron/x/interchaintxs/types" ) -// HandleAcknowledgement passes the acknowledgement data to the appropriate contract via a Sudo call. +// HandleAcknowledgement passes the acknowledgement data to the appropriate contract via a sudo call. func (im IBCModule) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) error { var ack channeltypes.Acknowledgement if err := channeltypes.SubModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { @@ -30,40 +26,28 @@ func (im IBCModule) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.P if err != nil { return errors.Wrapf(sdkerrors.ErrInvalidAddress, "failed to decode address from bech32: %v", err) } - if !im.ContractManagerKeeper.HasContractInfo(ctx, senderAddress) { + if !im.sudoKeeper.HasContractInfo(ctx, senderAddress) { return nil } im.wrappedKeeper.FeeKeeper.DistributeAcknowledgementFee(ctx, relayer, feetypes.NewPacketID(packet.SourcePort, packet.SourceChannel, packet.Sequence)) - cacheCtx, writeFn := neutronutils.CreateCachedContext(ctx, im.ContractManagerKeeper.GetParams(ctx).SudoCallGasLimit) - func() { - defer neutronerrors.OutOfGasRecovery(cacheCtx.GasMeter(), &err) - if ack.Success() { - _, err = im.ContractManagerKeeper.SudoResponse(cacheCtx, senderAddress, packet, ack.GetResult()) - } else { - // Actually we have only one kind of error returned from acknowledgement - // maybe later we'll retrieve actual errors from events - im.keeper.Logger(cacheCtx).Debug(ack.GetError(), "CheckTx", cacheCtx.IsCheckTx()) - _, err = im.ContractManagerKeeper.SudoError(cacheCtx, senderAddress, packet, ack.GetError()) - } - }() + msg, err := keeper.PrepareSudoCallbackMessage(packet, &ack) if err != nil { - // the contract either returned an error or panicked with `out of gas` - im.ContractManagerKeeper.AddContractFailure(ctx, &packet, senderAddress.String(), contractmanagertypes.Ack, &ack) - im.keeper.Logger(ctx).Debug("failed to Sudo contract on packet acknowledgement", err) - } else { - writeFn() + return errors.Wrapf(sdkerrors.ErrJSONMarshal, "failed to marshal Packet/Acknowledgment: %v", err) } - ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") + _, err = im.sudoKeeper.Sudo(ctx, senderAddress, msg) + if err != nil { + im.keeper.Logger(ctx).Debug("HandleAcknowledgement: failed to Sudo contract on packet acknowledgement", "error", err) + } im.keeper.Logger(ctx).Debug("acknowledgement received", "Packet data", data, "CheckTx", ctx.IsCheckTx()) return nil } -// HandleTimeout passes the timeout data to the appropriate contract via a Sudo call. +// HandleTimeout passes the timeout data to the appropriate contract via a sudo call. func (im IBCModule) HandleTimeout(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error { var data transfertypes.FungibleTokenPacketData if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { @@ -74,26 +58,21 @@ func (im IBCModule) HandleTimeout(ctx sdk.Context, packet channeltypes.Packet, r if err != nil { return errors.Wrapf(sdkerrors.ErrInvalidAddress, "failed to decode address from bech32: %v", err) } - if !im.ContractManagerKeeper.HasContractInfo(ctx, senderAddress) { + if !im.sudoKeeper.HasContractInfo(ctx, senderAddress) { return nil } + msg, err := keeper.PrepareSudoCallbackMessage(packet, nil) + if err != nil { + return errors.Wrapf(sdkerrors.ErrJSONMarshal, "failed to marshal Packet: %v", err) + } + im.wrappedKeeper.FeeKeeper.DistributeTimeoutFee(ctx, relayer, feetypes.NewPacketID(packet.SourcePort, packet.SourceChannel, packet.Sequence)) - cacheCtx, writeFn := neutronutils.CreateCachedContext(ctx, im.ContractManagerKeeper.GetParams(ctx).SudoCallGasLimit) - func() { - defer neutronerrors.OutOfGasRecovery(cacheCtx.GasMeter(), &err) - _, err = im.ContractManagerKeeper.SudoTimeout(cacheCtx, senderAddress, packet) - }() + _, err = im.sudoKeeper.Sudo(ctx, senderAddress, msg) if err != nil { - // the contract either returned an error or panicked with `out of gas` - im.ContractManagerKeeper.AddContractFailure(ctx, &packet, senderAddress.String(), contractmanagertypes.Timeout, nil) - im.keeper.Logger(ctx).Debug("failed to Sudo contract on packet timeout", err) - } else { - writeFn() + im.keeper.Logger(ctx).Debug("HandleAcknowledgement: failed to Sudo contract on packet acknowledgement", "error", err) } - ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") - return nil } diff --git a/x/transfer/ibc_handlers_test.go b/x/transfer/ibc_handlers_test.go index 191bbfa8d..03ebab6f7 100644 --- a/x/transfer/ibc_handlers_test.go +++ b/x/transfer/ibc_handlers_test.go @@ -2,7 +2,7 @@ package transfer_test import ( "fmt" - "github.com/neutron-org/neutron/x/contractmanager/types" + "github.com/neutron-org/neutron/x/contractmanager/keeper" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -20,37 +20,19 @@ import ( const TestCosmosAddress = "cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw" -var ( - ShouldNotBeWrittenKey = []byte("shouldnotkey") - ShouldNotBeWritten = []byte("should not be written") - ShouldBeWritten = []byte("should be written") -) - -func ShouldBeWrittenKey(suffix string) []byte { - return append([]byte("shouldkey"), []byte(suffix)...) -} - func TestHandleAcknowledgement(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) + wmKeeper := mock_types.NewMockWasmKeeper(ctrl) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) chanKeeper := mock_types.NewMockChannelKeeper(ctrl) authKeeper := mock_types.NewMockAccountKeeper(ctrl) // required to initialize keeper authKeeper.EXPECT().GetModuleAddress(transfertypes.ModuleName).Return([]byte("address")) - txKeeper, infCtx, storeKey := testkeeper.TransferKeeper(t, cmKeeper, feeKeeper, chanKeeper, authKeeper) - txModule := transfer.NewIBCModule(*txKeeper) + txKeeper, infCtx, _ := testkeeper.TransferKeeper(t, wmKeeper, feeKeeper, chanKeeper, authKeeper) + txModule := transfer.NewIBCModule(*txKeeper, wmKeeper) ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - store := ctx.KVStore(storeKey) - errACK := channeltypes.Acknowledgement{ - Response: &channeltypes.Acknowledgement_Error{ - Error: "error", - }, - } - errAckData, err := channeltypes.SubModuleCdc.MarshalJSON(&errACK) - require.NoError(t, err) resACK := channeltypes.Acknowledgement{ Response: &channeltypes.Acknowledgement_Result{Result: []byte("Result")}, } @@ -61,6 +43,7 @@ func TestHandleAcknowledgement(t *testing.T) { SourcePort: "transfer", SourceChannel: "channel-0", } + contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) relayerBech32 := "neutron1fxudpred77a0grgh69u0j7y84yks5ev4n5050z45kecz792jnd6scqu98z" relayerAddress := sdk.MustAccAddressFromBech32(relayerBech32) @@ -94,152 +77,44 @@ func TestHandleAcknowledgement(t *testing.T) { require.NoError(t, err) p.Data = tokenBz - // error during SudoResponse non contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) - err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) + msgAck, err := keeper.PrepareSudoCallbackMessage(p, &resACK) require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) - // error during SudoResponse contract + // non contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(ctx), contractAddress, p, resACK.GetResult()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) // consumes 2990 - }).Return(nil, fmt.Errorf("SudoResponse error")) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 5000}) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Ack, &resACK) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + wmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) - // error during SudoError non contract + // error during Sudo contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) - err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) - - // error during SudoError contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg string) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) // consumes 2990 - }).Return(nil, fmt.Errorf("SudoError error")) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 7000}) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Ack, &errACK) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) - - // success during SudoError non contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) - err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.NoError(t, err) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) - - // success during SudoError contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, err string) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldBeWrittenKey("sudoerror_contract"), ShouldBeWritten) - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 9000}) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.NoError(t, err) - require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudoerror_contract"))) - require.Equal(t, uint64(3320), ctx.GasMeter().GasConsumed()) - - // recoverable out of gas during SudoError non contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - require.NoError(t, err) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) - - // recoverable out of gas during SudoError contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, error string) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - cachedCtx.GasMeter().ConsumeGas(11001, "out of gas test") - }).Return(nil, fmt.Errorf("SudoError error")) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Ack, &errACK) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 11000}) - // FIXME: fix distribution during outofgas - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(11000), ctx.GasMeter().GasConsumed()) - - // check we have ReserveGas reserved and - // check gas consumption from cachedCtx has added to the main ctx - // one of the ways to check it - make the check during SudoResponse call - // non contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck).Return(nil, fmt.Errorf("SudoResponse error")) err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) - // contract + // success during Sudo contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - gasReserved := false - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(ctx), contractAddress, p, resACK.GetResult()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) { - if cachedCtx.GasMeter().Limit() == 13000 { - gasReserved = true - } - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldBeWrittenKey("sudoresponse_contract_success"), ShouldBeWritten) - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 13000}) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck) err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) - require.True(t, gasReserved) - require.Equal(t, uint64(3650), ctx.GasMeter().GasConsumed()) - require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudoresponse_contract_success"))) - - // not enough gas provided by relayer SudoCallGasLimit - lowGasCtx := infCtx.WithGasMeter(sdk.NewGasMeter(1000)) - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(lowGasCtx), contractAddress, p, resACK.GetResult()).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - cachedCtx.GasMeter().ConsumeGas(1001, "out of gas test") - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(lowGasCtx).Return(types.Params{SudoCallGasLimit: 14000}) - cmKeeper.EXPECT().HasContractInfo(lowGasCtx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeAcknowledgementFee(lowGasCtx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - require.PanicsWithValue(t, sdk.ErrorOutOfGas{Descriptor: "consume gas from cached context"}, func() { txModule.HandleAcknowledgement(lowGasCtx, p, resAckData, relayerAddress) }) //nolint:errcheck // this is a test - // NOTE: looks its impossible to test store reset after panic, because test `require.PanicsWithValue` recovers the panic - // require.Empty(t, store.Get(ShouldNotBeWrittenKey)) } func TestHandleTimeout(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) + wmKeeper := mock_types.NewMockWasmKeeper(ctrl) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) chanKeeper := mock_types.NewMockChannelKeeper(ctrl) authKeeper := mock_types.NewMockAccountKeeper(ctrl) // required to initialize keeper authKeeper.EXPECT().GetModuleAddress(transfertypes.ModuleName).Return([]byte("address")) - txKeeper, infCtx, storeKey := testkeeper.TransferKeeper(t, cmKeeper, feeKeeper, chanKeeper, authKeeper) - txModule := transfer.NewIBCModule(*txKeeper) + txKeeper, infCtx, _ := testkeeper.TransferKeeper(t, wmKeeper, feeKeeper, chanKeeper, authKeeper) + txModule := transfer.NewIBCModule(*txKeeper, wmKeeper) ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - store := ctx.KVStore(storeKey) contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) relayerBech32 := "neutron1fxudpred77a0grgh69u0j7y84yks5ev4n5050z45kecz792jnd6scqu98z" relayerAddress := sdk.MustAccAddressFromBech32(relayerBech32) @@ -274,88 +149,28 @@ func TestHandleTimeout(t *testing.T) { require.NoError(t, err) p.Data = tokenBz + msg, err := keeper.PrepareSudoCallbackMessage(p, nil) + require.NoError(t, err) + // success non contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) + wmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) err = txModule.HandleTimeout(ctx, p, relayerAddress) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) require.NoError(t, err) // success contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - gasReserved := false - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - if cachedCtx.GasMeter().Limit() == 5000 { - gasReserved = true - } - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldBeWrittenKey("sudotimeout_contract_success"), ShouldBeWritten) - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 5000}) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = txModule.HandleTimeout(ctx, p, relayerAddress) - require.True(t, gasReserved) - require.NoError(t, err) - require.Equal(t, uint64(3620), ctx.GasMeter().GasConsumed()) - require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudotimeout_contract_success"))) - - // error during SudoTimeOut non contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msg).Return(nil, nil) err = txModule.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) // error during SudoTimeOut contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - }).Return(nil, fmt.Errorf("SudoTimeout error")) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 7000}) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Timeout, nil) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - err = txModule.HandleTimeout(ctx, p, relayerAddress) - require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) - - // out of gas during SudoTimeOut non contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) - err = txModule.HandleTimeout(ctx, p, relayerAddress) - require.NoError(t, err) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) - - // out of gas during SudoTimeOut contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - cachedCtx.GasMeter().ConsumeGas(8001, "out of gas test") - }).Return(nil, fmt.Errorf("SudoTimeout error")) - cmKeeper.EXPECT().AddContractFailure(ctx, &p, contractAddress.String(), types.Timeout, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 8000}) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) + wmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msg).Return(nil, fmt.Errorf("SudoTimeout error")) err = txModule.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(8000), ctx.GasMeter().GasConsumed()) - - // not enough gas provided by relayer for SudoCallGasLimit - lowGasCtx := infCtx.WithGasMeter(sdk.NewGasMeter(1000)) - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(lowGasCtx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - store := cachedCtx.KVStore(storeKey) - store.Set(ShouldNotBeWrittenKey, ShouldNotBeWritten) - cachedCtx.GasMeter().ConsumeGas(1001, "out of gas test") - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(lowGasCtx).Return(types.Params{SudoCallGasLimit: 14000}) - cmKeeper.EXPECT().HasContractInfo(lowGasCtx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeTimeoutFee(lowGasCtx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - require.PanicsWithValue(t, sdk.ErrorOutOfGas{Descriptor: "consume gas from cached context"}, func() { txModule.HandleTimeout(lowGasCtx, p, relayerAddress) }) //nolint:errcheck // this is a test - // NOTE: looks its impossible to test store reset after panic, because test `require.PanicsWithValue` recovers the panic - // require.Empty(t, store.Get(ShouldNotBeWrittenKey)) } diff --git a/x/transfer/keeper/keeper.go b/x/transfer/keeper/keeper.go index 90f672939..50729540a 100644 --- a/x/transfer/keeper/keeper.go +++ b/x/transfer/keeper/keeper.go @@ -22,9 +22,9 @@ import ( // KeeperTransferWrapper is a wrapper for original ibc keeper to override response for "Transfer" method type KeeperTransferWrapper struct { keeper.Keeper - channelKeeper wrappedtypes.ChannelKeeper - FeeKeeper wrappedtypes.FeeRefunderKeeper - ContractManagerKeeper wrappedtypes.ContractManagerKeeper + channelKeeper wrappedtypes.ChannelKeeper + FeeKeeper wrappedtypes.FeeRefunderKeeper + SudoKeeper wrappedtypes.WasmKeeper } func (k KeeperTransferWrapper) Transfer(goCtx context.Context, msg *wrappedtypes.MsgTransfer) (*wrappedtypes.MsgTransferResponse, error) { @@ -46,7 +46,7 @@ func (k KeeperTransferWrapper) Transfer(goCtx context.Context, msg *wrappedtypes // if the sender is a contract, lock fees. // Because contracts are required to pay fees for the acknowledgements - if k.ContractManagerKeeper.HasContractInfo(ctx, senderAddr) { + if k.SudoKeeper.HasContractInfo(ctx, senderAddr) { if err := k.FeeKeeper.LockFees(ctx, senderAddr, feetypes.NewPacketID(msg.SourcePort, msg.SourceChannel, sequence), msg.Fee); err != nil { return nil, errors.Wrapf(err, "failed to lock fees to pay for transfer msg: %v", msg) } @@ -70,13 +70,13 @@ func NewKeeper( ics4Wrapper porttypes.ICS4Wrapper, channelKeeper wrappedtypes.ChannelKeeper, portKeeper types.PortKeeper, authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, feeKeeper wrappedtypes.FeeRefunderKeeper, - contractManagerKeeper wrappedtypes.ContractManagerKeeper, + sudoKeeper wrappedtypes.WasmKeeper, ) KeeperTransferWrapper { return KeeperTransferWrapper{ channelKeeper: channelKeeper, Keeper: keeper.NewKeeper(cdc, key, paramSpace, ics4Wrapper, channelKeeper, portKeeper, authKeeper, bankKeeper, scopedKeeper), - FeeKeeper: feeKeeper, - ContractManagerKeeper: contractManagerKeeper, + FeeKeeper: feeKeeper, + SudoKeeper: sudoKeeper, } } diff --git a/x/transfer/module.go b/x/transfer/module.go index 967fe459e..5b6bd8d82 100644 --- a/x/transfer/module.go +++ b/x/transfer/module.go @@ -27,19 +27,19 @@ import ( */ type IBCModule struct { - wrappedKeeper wrapkeeper.KeeperTransferWrapper - keeper keeper.Keeper - ContractManagerKeeper neutrontypes.ContractManagerKeeper + wrappedKeeper wrapkeeper.KeeperTransferWrapper + keeper keeper.Keeper + sudoKeeper neutrontypes.WasmKeeper transfer.IBCModule } // NewIBCModule creates a new IBCModule given the keeper -func NewIBCModule(k wrapkeeper.KeeperTransferWrapper) IBCModule { +func NewIBCModule(k wrapkeeper.KeeperTransferWrapper, sudoKeeper neutrontypes.WasmKeeper) IBCModule { return IBCModule{ - wrappedKeeper: k, - keeper: k.Keeper, - ContractManagerKeeper: k.ContractManagerKeeper, - IBCModule: transfer.NewIBCModule(k.Keeper), + wrappedKeeper: k, + keeper: k.Keeper, + sudoKeeper: sudoKeeper, + IBCModule: transfer.NewIBCModule(k.Keeper), } } diff --git a/x/transfer/types/expected_keepers.go b/x/transfer/types/expected_keepers.go index af269e1d8..854b8e490 100644 --- a/x/transfer/types/expected_keepers.go +++ b/x/transfer/types/expected_keepers.go @@ -4,19 +4,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" - feerefundertypes "github.com/neutron-org/neutron/x/feerefunder/types" ) -// ContractManagerKeeper defines the expected interface needed to add ack information about sudo failure. -type ContractManagerKeeper interface { +type WasmKeeper interface { HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool - AddContractFailure(ctx sdk.Context, packet *channeltypes.Packet, address, ackType string, ack *channeltypes.Acknowledgement) - SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, msg []byte) ([]byte, error) - SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, details string) ([]byte, error) - SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) - GetParams(ctx sdk.Context) (params contractmanagertypes.Params) + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) } type FeeRefunderKeeper interface {