From b9b41aeae99eb1601fc2ddee1cc8f5737d76a834 Mon Sep 17 00:00:00 2001 From: swelf Date: Fri, 8 Sep 2023 11:02:32 +0300 Subject: [PATCH 1/7] extracted "an executing sudo in cached limited context" into middleware --- app/app.go | 4 +- .../contractmanager/types/expected_keepers.go | 4 +- .../types/expected_keepers.go | 46 ---- .../interchaintxs/types/expected_keepers.go | 16 +- .../mocks/transfer/types/expected_keepers.go | 51 +---- wasmbinding/test/custom_message_test.go | 4 +- x/contractmanager/ibc_middleware.go | 79 +++++++ x/contractmanager/ibc_middleware_test.go | 82 +++++++ x/contractmanager/keeper/failure.go | 4 +- x/contractmanager/keeper/failure_test.go | 12 +- x/contractmanager/keeper/sudo.go | 78 +++---- x/contractmanager/keeper/sudo_test.go | 73 +----- x/contractmanager/types/module.go | 16 ++ x/contractmanager/types/sudo.go | 22 +- x/interchainqueries/types/expected_keepers.go | 4 - x/interchaintxs/keeper/ibc_handlers.go | 72 +++--- x/interchaintxs/keeper/ibc_handlers_test.go | 209 ++++++++---------- x/interchaintxs/types/expected_keepers.go | 6 +- x/transfer/ibc_handlers.go | 53 +---- x/transfer/ibc_handlers_test.go | 188 ++-------------- x/transfer/types/expected_keepers.go | 8 +- 21 files changed, 413 insertions(+), 618 deletions(-) create mode 100644 x/contractmanager/ibc_middleware.go create mode 100644 x/contractmanager/ibc_middleware_test.go create mode 100644 x/contractmanager/types/module.go diff --git a/app/app.go b/app/app.go index aaebe1645..c6dd3a84e 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.RouterKeeper.SetTransferKeeper(app.TransferKeeper.Keeper) @@ -660,7 +660,7 @@ func New( memKeys[interchaintxstypes.MemStoreKey], app.IBCKeeper.ChannelKeeper, app.ICAControllerKeeper, - app.ContractManagerKeeper, + contractmanager.NewSudoLimitWrapper(app.ContractManagerKeeper), app.FeeKeeper, ) diff --git a/testutil/mocks/contractmanager/types/expected_keepers.go b/testutil/mocks/contractmanager/types/expected_keepers.go index 463ef15c4..40b855702 100644 --- a/testutil/mocks/contractmanager/types/expected_keepers.go +++ b/testutil/mocks/contractmanager/types/expected_keepers.go @@ -51,7 +51,7 @@ func (mr *MockWasmKeeperMockRecorder) HasContractInfo(ctx, contractAddress inter // 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, "Sudo", ctx, contractAddress, msg) + ret := m.ctrl.Call(m, "sudo", ctx, contractAddress, msg) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 @@ -60,5 +60,5 @@ func (m *MockWasmKeeper) Sudo(ctx types.Context, contractAddress types.AccAddres // 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, "Sudo", reflect.TypeOf((*MockWasmKeeper)(nil).Sudo), ctx, contractAddress, msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "sudo", reflect.TypeOf((*MockWasmKeeper)(nil).Sudo), ctx, contractAddress, msg) } 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..85fa21af1 100644 --- a/testutil/mocks/interchaintxs/types/expected_keepers.go +++ b/testutil/mocks/interchaintxs/types/expected_keepers.go @@ -156,18 +156,18 @@ func (mr *MockContractManagerKeeperMockRecorder) HasContractInfo(ctx, contractAd } // SudoError mocks base method. -func (m *MockContractManagerKeeper) SudoError(ctx types.Context, senderAddress types.AccAddress, request types3.Packet, details string) ([]byte, error) { +func (m *MockContractManagerKeeper) SudoError(ctx types.Context, senderAddress types.AccAddress, request types3.Packet, ack types3.Acknowledgement) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoError", ctx, senderAddress, request, details) + ret := m.ctrl.Call(m, "SudoError", ctx, senderAddress, request, ack) 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 { +func (mr *MockContractManagerKeeperMockRecorder) SudoError(ctx, senderAddress, request, ack 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) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoError", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoError), ctx, senderAddress, request, ack) } // SudoOnChanOpenAck mocks base method. @@ -186,18 +186,18 @@ func (mr *MockContractManagerKeeperMockRecorder) SudoOnChanOpenAck(ctx, contract } // SudoResponse mocks base method. -func (m *MockContractManagerKeeper) SudoResponse(ctx types.Context, senderAddress types.AccAddress, request types3.Packet, msg []byte) ([]byte, error) { +func (m *MockContractManagerKeeper) SudoResponse(ctx types.Context, senderAddress types.AccAddress, request types3.Packet, ack types3.Acknowledgement) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, msg) + ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, ack) 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 *MockContractManagerKeeperMockRecorder) SudoResponse(ctx, senderAddress, request, ack 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, "SudoResponse", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoResponse), ctx, senderAddress, request, ack) } // SudoTimeout mocks base method. diff --git a/testutil/mocks/transfer/types/expected_keepers.go b/testutil/mocks/transfer/types/expected_keepers.go index f5c4d2ab1..122edaeb2 100644 --- a/testutil/mocks/transfer/types/expected_keepers.go +++ b/testutil/mocks/transfer/types/expected_keepers.go @@ -11,8 +11,7 @@ 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. @@ -38,32 +37,6 @@ func (m *MockContractManagerKeeper) EXPECT() *MockContractManagerKeeperMockRecor 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 { m.ctrl.T.Helper() @@ -79,33 +52,33 @@ func (mr *MockContractManagerKeeperMockRecorder) HasContractInfo(ctx, contractAd } // SudoError mocks base method. -func (m *MockContractManagerKeeper) SudoError(ctx types.Context, senderAddress types.AccAddress, request types1.Packet, details string) ([]byte, error) { +func (m *MockContractManagerKeeper) SudoError(ctx types.Context, senderAddress types.AccAddress, request types1.Packet, ack types1.Acknowledgement) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoError", ctx, senderAddress, request, details) + ret := m.ctrl.Call(m, "SudoError", ctx, senderAddress, request, ack) 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 { +func (mr *MockContractManagerKeeperMockRecorder) SudoError(ctx, senderAddress, request, ack 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) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoError", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoError), ctx, senderAddress, request, ack) } // SudoResponse mocks base method. -func (m *MockContractManagerKeeper) SudoResponse(ctx types.Context, senderAddress types.AccAddress, request types1.Packet, msg []byte) ([]byte, error) { +func (m *MockContractManagerKeeper) SudoResponse(ctx types.Context, senderAddress types.AccAddress, request types1.Packet, ack types1.Acknowledgement) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, msg) + ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, ack) 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 *MockContractManagerKeeperMockRecorder) SudoResponse(ctx, senderAddress, request, ack 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, "SudoResponse", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoResponse), ctx, senderAddress, request, ack) } // SudoTimeout mocks base method. @@ -147,7 +120,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 +132,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 +144,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/wasmbinding/test/custom_message_test.go b/wasmbinding/test/custom_message_test.go index d802dbe44..384b84cd6 100644 --- a/wasmbinding/test/custom_message_test.go +++ b/wasmbinding/test/custom_message_test.go @@ -640,7 +640,7 @@ func (suite *CustomMessengerTestSuite) TestResubmitFailureTimeout() { // 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, suite.contractAddress.String()) suite.messenger.ContractmanagerKeeper.AddContractFailure(suite.ctx, &packet, suite.contractAddress.String(), "timeout", &ack) @@ -673,7 +673,7 @@ 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) diff --git a/x/contractmanager/ibc_middleware.go b/x/contractmanager/ibc_middleware.go new file mode 100644 index 000000000..237c7f731 --- /dev/null +++ b/x/contractmanager/ibc_middleware.go @@ -0,0 +1,79 @@ +package contractmanager + +import ( + "fmt" + "github.com/cometbft/cometbft/libs/log" + sdk "github.com/cosmos/cosmos-sdk/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" + contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" +) + +type SudoLimitWrapper struct { + contractmanagertypes.ContractManagerWrapper +} + +// NewSudoLimitWrapper suppresses an error from a sudo contract handler and saves it to a store +func NewSudoLimitWrapper(keeper contractmanagertypes.ContractManagerWrapper) contractmanagertypes.ContractManagerWrapper { + return SudoLimitWrapper{ + keeper, + } +} + +func (k SudoLimitWrapper) SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) { + err := k.sudo(ctx, senderAddress, request, &ack) + if err != nil { + k.Logger(ctx).Debug("SudoLimitWrapper: failed to sudo contract", "error", err, "ackType", "Result") + } + return nil, nil +} + +func (k SudoLimitWrapper) SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) { + err := k.sudo(ctx, senderAddress, request, &ack) + if err != nil { + k.Logger(ctx).Debug("SudoLimitWrapper: failed to sudo contract", "error", err, "ackType", "ErrorSudoPayload") + } + return nil, nil +} + +func (k SudoLimitWrapper) SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) { + err := k.sudo(ctx, senderAddress, request, nil) + if err != nil { + k.Logger(ctx).Debug("SudoLimitWrapper: failed to sudo contract", "error", err, "ackType", "Timeout") + } + return nil, nil +} + +// 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 +func (k SudoLimitWrapper) sudo(ctx sdk.Context, sender sdk.AccAddress, packet channeltypes.Packet, ack *channeltypes.Acknowledgement) (err error) { + ackType := contractmanagertypes.Ack + cacheCtx, writeFn := neutronutils.CreateCachedContext(ctx, k.ContractManagerWrapper.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 == nil { + ackType = contractmanagertypes.Timeout + _, err = k.ContractManagerWrapper.SudoTimeout(cacheCtx, sender, packet) + } else if ack.GetError() != "" { + _, err = k.ContractManagerWrapper.SudoError(cacheCtx, sender, packet, *ack) + } else { + _, err = k.ContractManagerWrapper.SudoResponse(cacheCtx, sender, packet, *ack) + } + }() + if err != nil { + // the contract either returned an error or panicked with `out of gas` + k.ContractManagerWrapper.AddContractFailure(ctx, &packet, sender.String(), ackType, ack) + } 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)) +} diff --git a/x/contractmanager/ibc_middleware_test.go b/x/contractmanager/ibc_middleware_test.go new file mode 100644 index 000000000..57012be71 --- /dev/null +++ b/x/contractmanager/ibc_middleware_test.go @@ -0,0 +1,82 @@ +package contractmanager + +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" + 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" + "github.com/golang/mock/gomock" + mock_types "github.com/neutron-org/neutron/testutil/mocks/interchaintxs/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 NewSudoLimitMiddleware(t testing.TB, cm types.ContractManagerWrapper) (SudoLimitWrapper, 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()) + + k := SudoLimitWrapper{ContractManagerWrapper: cm} + + ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + + return k, ctx, storeKey +} + +func TestSudo(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) + middleware, infCtx, storeKey := NewSudoLimitMiddleware(t, cmKeeper) + st := infCtx.KVStore(storeKey) + + p := channeltypes.Packet{ + Sequence: 100, + SourcePort: icatypes.ControllerPortPrefix + TestOwnerAddress + ".ica0", + SourceChannel: "channel-0", + } + contractAddress := sdk.AccAddress{} + 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")}, + //} + //resAckData, err := channeltypes.SubModuleCdc.MarshalJSON(&resACK) + //require.NoError(t, err) + + // success during SudoError + ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) + cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, errAck channeltypes.Acknowledgement) { + st := cachedCtx.KVStore(storeKey) + st.Set(ShouldBeWrittenKey("sudoerror"), ShouldBeWritten) + }).Return(nil, nil) + cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 6000}) + err := middleware.sudo(ctx, contractAddress, p, &errACK) + require.NoError(t, err) + require.Equal(t, ShouldBeWritten, st.Get(ShouldBeWrittenKey("sudoerror"))) + require.Equal(t, uint64(3050), ctx.GasMeter().GasConsumed()) +} diff --git a/x/contractmanager/keeper/failure.go b/x/contractmanager/keeper/failure.go index 308decb34..581b4f19c 100644 --- a/x/contractmanager/keeper/failure.go +++ b/x/contractmanager/keeper/failure.go @@ -81,11 +81,11 @@ func (k Keeper) ResubmitFailure(ctx sdk.Context, contractAddr sdk.AccAddress, fa 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 { + if _, err := k.SudoResponse(ctx, contractAddr, *failure.Packet, *failure.Ack); 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 { + if _, err := k.SudoError(ctx, contractAddr, *failure.Packet, *failure.Ack); err != nil { return errors.Wrapf(types.FailedToResubmitFailure, "cannot resubmit failure ack error; failureId = %d; err = %s", failure.Id, err) } } diff --git a/x/contractmanager/keeper/failure_test.go b/x/contractmanager/keeper/failure_test.go index 606db61c9..aa00b4c91 100644 --- a/x/contractmanager/keeper/failure_test.go +++ b/x/contractmanager/keeper/failure_test.go @@ -138,7 +138,6 @@ func TestResubmitFailure(t *testing.T) { 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) failure, err := k.GetFailure(ctx, contractAddr, failureID) @@ -153,8 +152,7 @@ func TestResubmitFailure(t *testing.T) { failureID2 := k.GetNextFailureIDKey(ctx, contractAddr.String()) k.AddContractFailure(ctx, &packet, contractAddr.String(), types.Ack, &ack) - 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(gomock.AssignableToTypeOf(ctx), contractAddr, msgSuc).Return(nil, fmt.Errorf("failed to sudo")) failure2, err := k.GetFailure(ctx, contractAddr, failureID2) require.NoError(t, err) @@ -170,7 +168,6 @@ func TestResubmitFailure(t *testing.T) { failureID3 := k.GetNextFailureIDKey(ctx, contractAddr.String()) k.AddContractFailure(ctx, &packet, contractAddr.String(), types.Ack, &ackError) - 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) @@ -185,8 +182,7 @@ func TestResubmitFailure(t *testing.T) { failureID4 := k.GetNextFailureIDKey(ctx, contractAddr.String()) k.AddContractFailure(ctx, &packet, contractAddr.String(), types.Ack, &ackError) - 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) @@ -202,7 +198,6 @@ func TestResubmitFailure(t *testing.T) { failureID5 := k.GetNextFailureIDKey(ctx, contractAddr.String()) k.AddContractFailure(ctx, &packet, contractAddr.String(), "timeout", nil) - 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) @@ -217,8 +212,7 @@ func TestResubmitFailure(t *testing.T) { failureID6 := k.GetNextFailureIDKey(ctx, contractAddr.String()) k.AddContractFailure(ctx, &packet, contractAddr.String(), "timeout", nil) - 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) diff --git a/x/contractmanager/keeper/sudo.go b/x/contractmanager/keeper/sudo.go index 2feba728a..1b33639cb 100644 --- a/x/contractmanager/keeper/sudo.go +++ b/x/contractmanager/keeper/sudo.go @@ -20,26 +20,25 @@ func (k Keeper) HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) return k.wasmKeeper.HasContractInfo(ctx, contractAddress) } +func prepareSudoCallbackMessage(request channeltypes.Packet, ack *channeltypes.Acknowledgement) types.MessageSudoCallback { + m := types.MessageSudoCallback{ + Response: nil, + Error: nil, + Timeout: nil, + } + return m +} + func (k Keeper) SudoResponse( ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, - msg []byte, + ack channeltypes.Acknowledgement, ) ([]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 - } - 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) - } + k.Logger(ctx).Debug("SudoResponse", "senderAddress", senderAddress, "request", request, "msg", ack.GetResult()) x := types.MessageResponse{} - x.Response.Data = msg + x.Response.Data = ack.GetResult() x.Response.Request = request m, err := json.Marshal(x) if err != nil { @@ -50,9 +49,9 @@ func (k Keeper) SudoResponse( resp, err := k.wasmKeeper.Sudo(ctx, senderAddress, m) if err != nil { - k.Logger(ctx).Debug("SudoResponse: failed to Sudo", + k.Logger(ctx).Debug("SudoResponse: failed to sudo", "error", err, "contract_address", senderAddress) - return nil, fmt.Errorf("failed to Sudo: %v", err) + return nil, fmt.Errorf("failed to sudo: %v", err) } return resp, nil @@ -65,16 +64,6 @@ func (k Keeper) SudoTimeout( ) ([]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 - } - 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) @@ -88,9 +77,9 @@ func (k Keeper) SudoTimeout( resp, err := k.wasmKeeper.Sudo(ctx, senderAddress, m) if err != nil { - k.Logger(ctx).Debug("SudoTimeout: failed to Sudo", + 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 sudo: %v", err) } return resp, nil @@ -100,23 +89,13 @@ func (k Keeper) SudoError( ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, - details string, + ack channeltypes.Acknowledgement, ) ([]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 + x.Error.Details = ack.GetError() m, err := json.Marshal(x) if err != nil { k.Logger(ctx).Error("SudoError: failed to marshal MessageError message", @@ -126,9 +105,9 @@ func (k Keeper) SudoError( resp, err := k.wasmKeeper.Sudo(ctx, senderAddress, m) if err != nil { - k.Logger(ctx).Debug("SudoError: failed to Sudo", + k.Logger(ctx).Debug("SudoError: failed to sudo", "error", err, "contract_address", senderAddress) - return nil, fmt.Errorf("failed to Sudo: %v", err) + return nil, fmt.Errorf("failed to sudo: %v", err) } return resp, nil @@ -141,11 +120,6 @@ func (k Keeper) SudoOnChanOpenAck( ) ([]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) - } - x := types.MessageOnChanOpenAck{} x.OpenAck = details m, err := json.Marshal(x) @@ -158,9 +132,9 @@ func (k Keeper) SudoOnChanOpenAck( resp, err := k.wasmKeeper.Sudo(ctx, contractAddress, m) if err != nil { - k.Logger(ctx).Debug("SudoOnChanOpenAck: failed to Sudo", + k.Logger(ctx).Debug("SudoOnChanOpenAck: 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 @@ -198,9 +172,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 +206,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..8767acd93 100644 --- a/x/contractmanager/keeper/sudo_test.go +++ b/x/contractmanager/keeper/sudo_test.go @@ -48,34 +48,18 @@ func TestSudoResponse(t *testing.T) { sudoErrorMsg := types.MessageResponse{} p := channeltypes.Packet{} - sudoErrorMsg.Response.Data = []byte("data") + a := channeltypes.Acknowledgement{Response: &channeltypes.Acknowledgement_Result{Result: []byte("data")}} + sudoErrorMsg.Response.Data = a.GetResult() 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) + resp, err := k.SudoResponse(ctx, address, sudoErrorMsg.Response.Request, a) 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) + resp, err = k.SudoResponse(ctx, address, sudoErrorMsg.Response.Request, a) 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) { @@ -88,34 +72,20 @@ func TestSudoError(t *testing.T) { sudoErrorMsg := types.MessageError{} p := channeltypes.Packet{} - sudoErrorMsg.Error.Details = "details" + a := channeltypes.Acknowledgement{Response: &channeltypes.Acknowledgement_Error{ + Error: "details", + }} + sudoErrorMsg.Error.Details = a.GetError() 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) + resp, err := k.SudoError(ctx, address, sudoErrorMsg.Error.Request, a) 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) + resp, err = k.SudoError(ctx, address, sudoErrorMsg.Error.Request, a) 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) { @@ -130,30 +100,14 @@ func TestSudoTimeout(t *testing.T) { 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) { @@ -166,21 +120,14 @@ func TestSudoOnChanOpen(t *testing.T) { 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) { diff --git a/x/contractmanager/types/module.go b/x/contractmanager/types/module.go new file mode 100644 index 000000000..540b2afd3 --- /dev/null +++ b/x/contractmanager/types/module.go @@ -0,0 +1,16 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" +) + +type ContractManagerWrapper 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, ack channeltypes.Acknowledgement) ([]byte, error) + SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) + SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) + SudoOnChanOpenAck(ctx sdk.Context, contractAddress sdk.AccAddress, details OpenAckDetails) ([]byte, error) + GetParams(ctx sdk.Context) (params Params) +} diff --git a/x/contractmanager/types/sudo.go b/x/contractmanager/types/sudo.go index c0564d82a..8b2d0144d 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,6 +23,26 @@ type MessageKVQueryResult struct { } `json:"kv_query_result"` } +type MessageSudoCallback struct { + Response *ResponseSudoPayload `json:"response"` + Error *ErrorSudoPayload `json:"error"` + Timeout *TimeoutPayload `json:"timeout"` +} + +type ResponseSudoPayload struct { + Request channeltypes.Packet `json:"request"` + Data []byte `json:"data"` // Message data +} + +type ErrorSudoPayload struct { + Request channeltypes.Packet `json:"request"` + Details string `json:"details"` +} + +type TimeoutPayload struct { + Request channeltypes.Packet `json:"request"` +} + // MessageTimeout is passed to a contract's sudo() entrypoint when an interchain // transaction failed with a timeout. type MessageTimeout struct { 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/keeper/ibc_handlers.go b/x/interchaintxs/keeper/ibc_handlers.go index d1deab8fa..4fe930e1f 100644 --- a/x/interchaintxs/keeper/ibc_handlers.go +++ b/x/interchaintxs/keeper/ibc_handlers.go @@ -1,8 +1,6 @@ package keeper import ( - "github.com/neutron-org/neutron/neutronutils" - neutronerrors "github.com/neutron-org/neutron/neutronutils/errors" "time" "cosmossdk.io/errors" @@ -17,7 +15,7 @@ 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) @@ -34,63 +32,48 @@ func (k *Keeper) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.Pack return errors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-27 packet acknowledgement: %v", err) } + if !k.contractManagerKeeper.HasContractInfo(ctx, icaOwner.GetContract()) { + //return fmt.Errorf("%s is not a contract address", icaOwner.GetContract()) + return nil + } + 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()) - } - }() - 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) + // Actually we have only one kind of error returned from acknowledgement + // maybe later we'll retrieve actual errors from events + // `err` value from `SudoError/SudoResponse` should always be nil, since we contractmanager wrapped by `SudoLimitWrapper` + if ack.GetError() != "" { + _, err = k.contractManagerKeeper.SudoError(ctx, icaOwner.GetContract(), packet, ack) } else { - writeFn() + _, err = k.contractManagerKeeper.SudoResponse(ctx, icaOwner.GetContract(), packet, ack) } - ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") - - return nil + return err } -// 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) + k.Logger(ctx).Debug("HandleTimeout") icaOwner, err := types.ICAOwnerFromPort(packet.SourcePort) - k.Logger(ctx).Debug("HandleTimeout") 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") } - 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) - }() - 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() + if !k.contractManagerKeeper.HasContractInfo(ctx, icaOwner.GetContract()) { + //return fmt.Errorf("%s is not a contract address", icaOwner.GetContract()) + return nil } - ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") + k.feeKeeper.DistributeTimeoutFee(ctx, relayer, feetypes.NewPacketID(packet.SourcePort, packet.SourceChannel, packet.Sequence)) + + // `err` value from `SudoTimeout` should always be nil, since we contractmanager wrapped by `SudoLimitWrapper` + _, err = k.contractManagerKeeper.SudoTimeout(ctx, icaOwner.GetContract(), packet) - return nil + return err } // HandleChanOpenAck passes the data about a successfully created channel to the appropriate contract @@ -113,6 +96,11 @@ func (k *Keeper) HandleChanOpenAck( return errors.Wrap(err, "failed to get ica owner from port") } + if !k.contractManagerKeeper.HasContractInfo(ctx, icaOwner.GetContract()) { + //return fmt.Errorf("%s is not a contract address", icaOwner.GetContract()) + return nil + } + _, err = k.contractManagerKeeper.SudoOnChanOpenAck(ctx, icaOwner.GetContract(), contractmanagertypes.OpenAckDetails{ PortID: portID, ChannelID: channelID, @@ -120,8 +108,8 @@ func (k *Keeper) HandleChanOpenAck( CounterpartyVersion: counterpartyVersion, }) 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 packet timeout", "error", err) + return errors.Wrap(err, "failed to sudo the contract OnChanOpenAck") } return nil diff --git a/x/interchaintxs/keeper/ibc_handlers_test.go b/x/interchaintxs/keeper/ibc_handlers_test.go index 85f435d4b..50e74b2f0 100644 --- a/x/interchaintxs/keeper/ibc_handlers_test.go +++ b/x/interchaintxs/keeper/ibc_handlers_test.go @@ -17,25 +17,15 @@ 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) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) - icak, infCtx, storeKey := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil) + icak, infCtx, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil) ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - store := ctx.KVStore(storeKey) + //store := ctx.KVStore(storeKey) errACK := channeltypes.Acknowledgement{ Response: &channeltypes.Acknowledgement_Error{ @@ -64,86 +54,96 @@ 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 + // success 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(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) + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + cmKeeper.EXPECT().SudoResponse(ctx, contractAddress, p, resACK) err = icak.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 + // success contract 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(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) + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + cmKeeper.EXPECT().SudoError(ctx, contractAddress, p, errACK) 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 + // error 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(ShouldBeWrittenKey("sudoerror"), ShouldBeWritten) - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 6000}) + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) 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()) + cmKeeper.EXPECT().SudoResponse(ctx, contractAddress, p, resACK).Return(nil, fmt.Errorf("error sudoResponse")) + err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) + require.Error(t, err) - // out of gas during SudoError + // error contract 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) + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + cmKeeper.EXPECT().SudoError(ctx, contractAddress, p, errACK).Return(nil, fmt.Errorf("error sudoError")) err = icak.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(7000), ctx.GasMeter().GasConsumed()) + require.Error(t, err) - // success during SudoResponse + // no contract SudoError 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)) - err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) + err = icak.HandleAcknowledgement(ctx, p, errAckData, 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 + //// 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) + //require.NoError(t, err) + //require.Empty(t, store.Get(ShouldNotBeWrittenKey)) + //require.Equal(t, uint64(7000), ctx.GasMeter().GasConsumed()) + // + //// success 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(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)) + //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) { @@ -152,9 +152,8 @@ func TestHandleTimeout(t *testing.T) { icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) - icak, infCtx, storeKey := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil) + icak, infCtx, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil) 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) @@ -167,63 +166,27 @@ func TestHandleTimeout(t *testing.T) { err := icak.HandleTimeout(ctx, channeltypes.Packet{}, relayerAddress) require.ErrorContains(t, err, "failed to get ica owner from port") - gasReserved := false + // 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) { - 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}) + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + cmKeeper.EXPECT().SudoTimeout(ctx, contractAddress, p) 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"))) require.NoError(t, err) - // error 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) - }).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) + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + cmKeeper.EXPECT().SudoTimeout(ctx, contractAddress, p).Return(nil, fmt.Errorf("SudoTimeout error")) err = icak.HandleTimeout(ctx, p, relayerAddress) - require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) - require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) + require.Error(t, err) - // out of gas during SudoTimeOut + // no 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(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)) + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) 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) { @@ -239,6 +202,7 @@ 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().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) cmKeeper.EXPECT().SudoOnChanOpenAck(ctx, contractAddress, types.OpenAckDetails{ PortID: portID, ChannelID: channelID, @@ -246,8 +210,9 @@ func TestHandleChanOpenAck(t *testing.T) { CounterpartyVersion: "1", }).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.ErrorContains(t, err, "failed to sudo the contract OnChanOpenAck") + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) cmKeeper.EXPECT().SudoOnChanOpenAck(ctx, contractAddress, types.OpenAckDetails{ PortID: portID, ChannelID: channelID, @@ -256,4 +221,8 @@ func TestHandleChanOpenAck(t *testing.T) { }).Return(nil, nil) err = icak.HandleChanOpenAck(ctx, portID, channelID, counterpartyChannelID, "1") require.NoError(t, err) + + cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) + err = icak.HandleChanOpenAck(ctx, portID, channelID, counterpartyChannelID, "1") + require.NoError(t, err) } diff --git a/x/interchaintxs/types/expected_keepers.go b/x/interchaintxs/types/expected_keepers.go index a9be5877b..13d7f7cf3 100644 --- a/x/interchaintxs/types/expected_keepers.go +++ b/x/interchaintxs/types/expected_keepers.go @@ -26,11 +26,11 @@ type BankKeeper interface { type ContractManagerKeeper 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) + SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) + SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]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) + AddContractFailure(ctx sdk.Context, packet *channeltypes.Packet, address, ackType string, ack *channeltypes.Acknowledgement) GetParams(ctx sdk.Context) (params contractmanagertypes.Params) } diff --git a/x/transfer/ibc_handlers.go b/x/transfer/ibc_handlers.go index a3a7b72ed..efecb0cff 100644 --- a/x/transfer/ibc_handlers.go +++ b/x/transfer/ibc_handlers.go @@ -1,21 +1,16 @@ 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" 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 { @@ -36,34 +31,21 @@ func (im IBCModule) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.P 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()) - } - }() - 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) + if ack.Success() { + _, err = im.ContractManagerKeeper.SudoResponse(ctx, senderAddress, packet, ack) } else { - writeFn() + // Actually we have only one kind of error returned from acknowledgement + // maybe later we'll retrieve actual errors from events + im.keeper.Logger(ctx).Debug(ack.GetError(), "CheckTx", ctx.IsCheckTx()) + _, err = im.ContractManagerKeeper.SudoError(ctx, senderAddress, packet, ack) } - ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") - im.keeper.Logger(ctx).Debug("acknowledgement received", "Packet data", data, "CheckTx", ctx.IsCheckTx()) - return nil + return err } -// 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 { @@ -80,20 +62,7 @@ func (im IBCModule) HandleTimeout(ctx sdk.Context, packet channeltypes.Packet, r 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) - }() - 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() - } - - ctx.GasMeter().ConsumeGas(cacheCtx.GasMeter().GasConsumedToLimit(), "consume gas from cached context") + _, err = im.ContractManagerKeeper.SudoTimeout(ctx, senderAddress, packet) - return nil + return err } diff --git a/x/transfer/ibc_handlers_test.go b/x/transfer/ibc_handlers_test.go index 191bbfa8d..e93d91c97 100644 --- a/x/transfer/ibc_handlers_test.go +++ b/x/transfer/ibc_handlers_test.go @@ -2,7 +2,6 @@ package transfer_test import ( "fmt" - "github.com/neutron-org/neutron/x/contractmanager/types" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -39,10 +38,9 @@ func TestHandleAcknowledgement(t *testing.T) { 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) + txKeeper, infCtx, _ := testkeeper.TransferKeeper(t, cmKeeper, feeKeeper, chanKeeper, authKeeper) txModule := transfer.NewIBCModule(*txKeeper) ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - store := ctx.KVStore(storeKey) errACK := channeltypes.Acknowledgement{ Response: &channeltypes.Acknowledgement_Error{ @@ -94,137 +92,43 @@ func TestHandleAcknowledgement(t *testing.T) { require.NoError(t, err) p.Data = tokenBz - // error during SudoResponse non contract + // 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) require.NoError(t, err) - require.Empty(t, store.Get(ShouldNotBeWrittenKey)) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) // error during SudoResponse 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)) + cmKeeper.EXPECT().SudoResponse(ctx, contractAddress, p, resACK).Return(nil, fmt.Errorf("SudoResponse error")) 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 - 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()) + require.Error(t, err) // 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)) + cmKeeper.EXPECT().SudoError(ctx, contractAddress, p, errACK).Return(nil, fmt.Errorf("SudoError error")) 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()) + require.Error(t, err) - // success during SudoError non contract + // success during SudoError 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) - 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)) + cmKeeper.EXPECT().SudoError(ctx, contractAddress, p, errACK) 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) - err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) - require.NoError(t, err) - require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) - - // contract + // success during SudoError 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) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + cmKeeper.EXPECT().SudoResponse(ctx, contractAddress, p, resACK) 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) { @@ -236,10 +140,9 @@ func TestHandleTimeout(t *testing.T) { 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) + txKeeper, infCtx, _ := testkeeper.TransferKeeper(t, cmKeeper, feeKeeper, chanKeeper, authKeeper) txModule := transfer.NewIBCModule(*txKeeper) 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) @@ -278,84 +181,21 @@ func TestHandleTimeout(t *testing.T) { 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.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) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + cmKeeper.EXPECT().SudoTimeout(ctx, contractAddress, p).Return(nil, nil) 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) - 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) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) + cmKeeper.EXPECT().SudoTimeout(ctx, contractAddress, p).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)) + require.Error(t, err) } diff --git a/x/transfer/types/expected_keepers.go b/x/transfer/types/expected_keepers.go index af269e1d8..774daf841 100644 --- a/x/transfer/types/expected_keepers.go +++ b/x/transfer/types/expected_keepers.go @@ -4,19 +4,15 @@ 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 { 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) + SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) + SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) - GetParams(ctx sdk.Context) (params contractmanagertypes.Params) } type FeeRefunderKeeper interface { From 5bdacb494033abc0e2518459899321df214d68e0 Mon Sep 17 00:00:00 2001 From: swelf Date: Thu, 14 Sep 2023 12:33:58 +0300 Subject: [PATCH 2/7] combined SudoResponse/Error/Timeout into Sudo --- Makefile | 2 +- app/app.go | 9 +- proto/neutron/contractmanager/failure.proto | 8 +- .../interchaintxs/keeper/interchaintxs.go | 2 +- .../contractmanager/types/expected_keepers.go | 4 +- .../interchaintxs/types/expected_keepers.go | 120 +++-------- .../mocks/transfer/types/expected_keepers.go | 70 ++----- testutil/transfer/keeper/keeper.go | 2 +- wasmbinding/test/custom_message_test.go | 18 +- .../client/cli/query_failure_test.go | 6 +- x/contractmanager/genesis.go | 2 +- x/contractmanager/genesis_test.go | 33 +-- x/contractmanager/ibc_middleware.go | 56 ++--- x/contractmanager/ibc_middleware_test.go | 161 +++++++-------- x/contractmanager/keeper/failure.go | 37 +--- x/contractmanager/keeper/failure_test.go | 76 +++---- x/contractmanager/keeper/sudo.go | 104 ++-------- x/contractmanager/keeper/sudo_test.go | 110 ---------- x/contractmanager/types/failure.pb.go | 191 ++++-------------- x/contractmanager/types/module.go | 11 +- x/contractmanager/types/sudo.go | 34 +--- x/interchaintxs/keeper/ibc_handlers.go | 46 ++--- x/interchaintxs/keeper/ibc_handlers_test.go | 108 ++++------ x/interchaintxs/keeper/keeper.go | 30 +-- x/interchaintxs/keeper/msg_server.go | 4 +- x/interchaintxs/keeper/msg_server_test.go | 30 +-- x/interchaintxs/types/expected_keepers.go | 10 +- x/transfer/ibc_handlers.go | 34 ++-- x/transfer/ibc_handlers_test.go | 79 +++----- x/transfer/keeper/keeper.go | 14 +- x/transfer/module.go | 16 +- x/transfer/types/expected_keepers.go | 7 +- 32 files changed, 454 insertions(+), 980 deletions(-) diff --git a/Makefile b/Makefile index 6af490db8..1bdcceca1 100644 --- a/Makefile +++ b/Makefile @@ -123,7 +123,7 @@ build-static-linux-amd64: go.sum $(BUILDDIR)/ $(DOCKER) cp neutronbinary:/bin/neutrond $(BUILDDIR)/neutrond-linux-amd64 $(DOCKER) rm -f neutronbinary -install-test-binary: check_version go.sum +install-test-binary: go.sum go install -mod=readonly $(BUILD_FLAGS_TEST_BINARY) ./cmd/neutrond ######################################## diff --git a/app/app.go b/app/app.go index c6dd3a84e..6804471df 100644 --- a/app/app.go +++ b/app/app.go @@ -563,7 +563,7 @@ func New( app.BankKeeper, scopedTransferKeeper, app.FeeKeeper, - contractmanager.NewSudoLimitWrapper(app.ContractManagerKeeper), + contractmanager.NewSudoLimitWrapper(app.ContractManagerKeeper, &app.WasmKeeper), ) app.RouterKeeper.SetTransferKeeper(app.TransferKeeper.Keeper) @@ -660,7 +660,7 @@ func New( memKeys[interchaintxstypes.MemStoreKey], app.IBCKeeper.ChannelKeeper, app.ICAControllerKeeper, - contractmanager.NewSudoLimitWrapper(app.ContractManagerKeeper), + contractmanager.NewSudoLimitWrapper(app.ContractManagerKeeper, &app.WasmKeeper), app.FeeKeeper, ) @@ -699,7 +699,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/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/testutil/interchaintxs/keeper/interchaintxs.go b/testutil/interchaintxs/keeper/interchaintxs.go index 55c9b43a9..4961a46f9 100644 --- a/testutil/interchaintxs/keeper/interchaintxs.go +++ b/testutil/interchaintxs/keeper/interchaintxs.go @@ -17,7 +17,7 @@ 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) (*keeper.Keeper, sdk.Context, *storetypes.KVStoreKey) { storeKey := sdk.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) diff --git a/testutil/mocks/contractmanager/types/expected_keepers.go b/testutil/mocks/contractmanager/types/expected_keepers.go index 40b855702..463ef15c4 100644 --- a/testutil/mocks/contractmanager/types/expected_keepers.go +++ b/testutil/mocks/contractmanager/types/expected_keepers.go @@ -51,7 +51,7 @@ func (mr *MockWasmKeeperMockRecorder) HasContractInfo(ctx, contractAddress inter // 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, "sudo", ctx, contractAddress, msg) + ret := m.ctrl.Call(m, "Sudo", ctx, contractAddress, msg) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 @@ -60,5 +60,5 @@ func (m *MockWasmKeeper) Sudo(ctx types.Context, contractAddress types.AccAddres // 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, "sudo", reflect.TypeOf((*MockWasmKeeper)(nil).Sudo), ctx, contractAddress, msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sudo", reflect.TypeOf((*MockWasmKeeper)(nil).Sudo), ctx, contractAddress, msg) } diff --git a/testutil/mocks/interchaintxs/types/expected_keepers.go b/testutil/mocks/interchaintxs/types/expected_keepers.go index 85fa21af1..d42f778a0 100644 --- a/testutil/mocks/interchaintxs/types/expected_keepers.go +++ b/testutil/mocks/interchaintxs/types/expected_keepers.go @@ -14,8 +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" - types5 "github.com/neutron-org/neutron/x/feerefunder/types" + types4 "github.com/neutron-org/neutron/x/feerefunder/types" ) // MockAccountKeeper is a mock of AccountKeeper interface. @@ -92,57 +91,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 +123,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 types3.Packet, ack types3.Acknowledgement) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoError", ctx, senderAddress, request, ack) - 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, ack interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoError", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoError), ctx, senderAddress, request, ack) -} - -// 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, ack types3.Acknowledgement) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, ack) - 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, ack 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, ack) + 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 types3.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) } // MockICAControllerKeeper is a mock of ICAControllerKeeper interface. @@ -321,7 +249,7 @@ func (m *MockFeeRefunderKeeper) EXPECT() *MockFeeRefunderKeeperMockRecorder { } // DistributeAcknowledgementFee mocks base method. -func (m *MockFeeRefunderKeeper) DistributeAcknowledgementFee(ctx types.Context, receiver types.AccAddress, packetID types5.PacketID) { +func (m *MockFeeRefunderKeeper) DistributeAcknowledgementFee(ctx types.Context, receiver types.AccAddress, packetID types4.PacketID) { m.ctrl.T.Helper() m.ctrl.Call(m, "DistributeAcknowledgementFee", ctx, receiver, packetID) } @@ -333,7 +261,7 @@ func (mr *MockFeeRefunderKeeperMockRecorder) DistributeAcknowledgementFee(ctx, r } // DistributeTimeoutFee mocks base method. -func (m *MockFeeRefunderKeeper) DistributeTimeoutFee(ctx types.Context, receiver types.AccAddress, packetID types5.PacketID) { +func (m *MockFeeRefunderKeeper) DistributeTimeoutFee(ctx types.Context, receiver types.AccAddress, packetID types4.PacketID) { m.ctrl.T.Helper() m.ctrl.Call(m, "DistributeTimeoutFee", ctx, receiver, packetID) } @@ -345,7 +273,7 @@ func (mr *MockFeeRefunderKeeperMockRecorder) DistributeTimeoutFee(ctx, receiver, } // LockFees mocks base method. -func (m *MockFeeRefunderKeeper) LockFees(ctx types.Context, payer types.AccAddress, packetID types5.PacketID, fee types5.Fee) error { +func (m *MockFeeRefunderKeeper) LockFees(ctx types.Context, payer types.AccAddress, packetID types4.PacketID, fee types4.Fee) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockFees", ctx, payer, packetID, fee) ret0, _ := ret[0].(error) diff --git a/testutil/mocks/transfer/types/expected_keepers.go b/testutil/mocks/transfer/types/expected_keepers.go index 122edaeb2..3123b6eb5 100644 --- a/testutil/mocks/transfer/types/expected_keepers.go +++ b/testutil/mocks/transfer/types/expected_keepers.go @@ -14,31 +14,31 @@ import ( 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 } // 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) @@ -46,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 { +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 types1.Packet, ack types1.Acknowledgement) ([]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, ack) + 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, ack 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, ack) -} - -// SudoResponse mocks base method. -func (m *MockContractManagerKeeper) SudoResponse(ctx types.Context, senderAddress types.AccAddress, request types1.Packet, ack types1.Acknowledgement) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SudoResponse", ctx, senderAddress, request, ack) - 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, ack interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SudoResponse", reflect.TypeOf((*MockContractManagerKeeper)(nil).SudoResponse), ctx, senderAddress, request, ack) -} - -// SudoTimeout mocks base method. -func (m *MockContractManagerKeeper) SudoTimeout(ctx types.Context, senderAddress types.AccAddress, request types1.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) } // MockFeeRefunderKeeper is a mock of FeeRefunderKeeper interface. 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/test/custom_message_test.go b/wasmbinding/test/custom_message_test.go index 384b84cd6..1f5d11d5b 100644 --- a/wasmbinding/test/custom_message_test.go +++ b/wasmbinding/test/custom_message_test.go @@ -3,10 +3,9 @@ package test import ( "encoding/json" "fmt" + keeper2 "github.com/neutron-org/neutron/x/contractmanager/keeper" "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" @@ -609,8 +608,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 +640,10 @@ func (suite *CustomMessengerTestSuite) TestResubmitFailureTimeout() { // Add failure packet := ibcchanneltypes.Packet{} - ack := ibcchanneltypes.Acknowledgement{ - Response: &ibcchanneltypes.Acknowledgement_Error{Error: "ErrorSudoPayload"}, - } + 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{ @@ -676,7 +676,9 @@ func (suite *CustomMessengerTestSuite) TestResubmitFailureFromDifferentContract( 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 index 237c7f731..841897459 100644 --- a/x/contractmanager/ibc_middleware.go +++ b/x/contractmanager/ibc_middleware.go @@ -4,68 +4,40 @@ import ( "fmt" "github.com/cometbft/cometbft/libs/log" sdk "github.com/cosmos/cosmos-sdk/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" contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" ) type SudoLimitWrapper struct { - contractmanagertypes.ContractManagerWrapper + contractManager keeper.Keeper + contractmanagertypes.WasmKeeper } -// NewSudoLimitWrapper suppresses an error from a sudo contract handler and saves it to a store -func NewSudoLimitWrapper(keeper contractmanagertypes.ContractManagerWrapper) contractmanagertypes.ContractManagerWrapper { +// NewSudoLimitWrapper suppresses an error from a Sudo contract handler and saves it to a store +func NewSudoLimitWrapper(contractManager keeper.Keeper, sudoKeeper contractmanagertypes.WasmKeeper) contractmanagertypes.WasmKeeper { return SudoLimitWrapper{ - keeper, + contractManager, + sudoKeeper, } } -func (k SudoLimitWrapper) SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) { - err := k.sudo(ctx, senderAddress, request, &ack) - if err != nil { - k.Logger(ctx).Debug("SudoLimitWrapper: failed to sudo contract", "error", err, "ackType", "Result") - } - return nil, nil -} - -func (k SudoLimitWrapper) SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) { - err := k.sudo(ctx, senderAddress, request, &ack) - if err != nil { - k.Logger(ctx).Debug("SudoLimitWrapper: failed to sudo contract", "error", err, "ackType", "ErrorSudoPayload") - } - return nil, nil -} - -func (k SudoLimitWrapper) SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) { - err := k.sudo(ctx, senderAddress, request, nil) - if err != nil { - k.Logger(ctx).Debug("SudoLimitWrapper: failed to sudo contract", "error", err, "ackType", "Timeout") - } - return nil, nil -} - -// sudo calls underlying sudo handlers with a limited amount of gas +// 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 -func (k SudoLimitWrapper) sudo(ctx sdk.Context, sender sdk.AccAddress, packet channeltypes.Packet, ack *channeltypes.Acknowledgement) (err error) { - ackType := contractmanagertypes.Ack - cacheCtx, writeFn := neutronutils.CreateCachedContext(ctx, k.ContractManagerWrapper.GetParams(ctx).SudoCallGasLimit) +// 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 := neutronutils.CreateCachedContext(ctx, k.contractManager.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 == nil { - ackType = contractmanagertypes.Timeout - _, err = k.ContractManagerWrapper.SudoTimeout(cacheCtx, sender, packet) - } else if ack.GetError() != "" { - _, err = k.ContractManagerWrapper.SudoError(cacheCtx, sender, packet, *ack) - } else { - _, err = k.ContractManagerWrapper.SudoResponse(cacheCtx, sender, packet, *ack) - } + resp, err = k.WasmKeeper.Sudo(cacheCtx, contractAddress, msg) + }() if err != nil { // the contract either returned an error or panicked with `out of gas` - k.ContractManagerWrapper.AddContractFailure(ctx, &packet, sender.String(), ackType, ack) + k.contractManager.AddContractFailure(ctx, contractAddress.String(), msg) } else { writeFn() } diff --git a/x/contractmanager/ibc_middleware_test.go b/x/contractmanager/ibc_middleware_test.go index 57012be71..b1b964fbf 100644 --- a/x/contractmanager/ibc_middleware_test.go +++ b/x/contractmanager/ibc_middleware_test.go @@ -1,82 +1,83 @@ package contractmanager -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" - 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" - "github.com/golang/mock/gomock" - mock_types "github.com/neutron-org/neutron/testutil/mocks/interchaintxs/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 NewSudoLimitMiddleware(t testing.TB, cm types.ContractManagerWrapper) (SudoLimitWrapper, 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()) - - k := SudoLimitWrapper{ContractManagerWrapper: cm} - - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) - - return k, ctx, storeKey -} - -func TestSudo(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) - middleware, infCtx, storeKey := NewSudoLimitMiddleware(t, cmKeeper) - st := infCtx.KVStore(storeKey) - - p := channeltypes.Packet{ - Sequence: 100, - SourcePort: icatypes.ControllerPortPrefix + TestOwnerAddress + ".ica0", - SourceChannel: "channel-0", - } - contractAddress := sdk.AccAddress{} - 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")}, - //} - //resAckData, err := channeltypes.SubModuleCdc.MarshalJSON(&resACK) - //require.NoError(t, err) - - // success during SudoError - ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, errAck channeltypes.Acknowledgement) { - st := cachedCtx.KVStore(storeKey) - st.Set(ShouldBeWrittenKey("sudoerror"), ShouldBeWritten) - }).Return(nil, nil) - cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 6000}) - err := middleware.sudo(ctx, contractAddress, p, &errACK) - require.NoError(t, err) - require.Equal(t, ShouldBeWritten, st.Get(ShouldBeWrittenKey("sudoerror"))) - require.Equal(t, uint64(3050), ctx.GasMeter().GasConsumed()) -} +// +//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" +// 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" +// "github.com/golang/mock/gomock" +// mock_types "github.com/neutron-org/neutron/testutil/mocks/interchaintxs/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 NewSudoLimitMiddleware(t testing.TB, cm types.SudoWrapper) (SudoLimitWrapper, 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()) +// +// k := SudoLimitWrapper{SudoWrapper: cm} +// +// ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) +// +// return k, ctx, storeKey +//} +// +//func TestSudo(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) +// middleware, infCtx, storeKey := NewSudoLimitMiddleware(t, cmKeeper) +// st := infCtx.KVStore(storeKey) +// +// p := channeltypes.Packet{ +// Sequence: 100, +// SourcePort: icatypes.ControllerPortPrefix + TestOwnerAddress + ".ica0", +// SourceChannel: "channel-0", +// } +// contractAddress := sdk.AccAddress{} +// 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")}, +// //} +// //resAckData, err := channeltypes.SubModuleCdc.MarshalJSON(&resACK) +// //require.NoError(t, err) +// +// // success during SudoError +// ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) +// cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, errAck channeltypes.Acknowledgement) { +// st := cachedCtx.KVStore(storeKey) +// st.Set(ShouldBeWrittenKey("sudoerror"), ShouldBeWritten) +// }).Return(nil, nil) +// cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 6000}) +// err := middleware.Sudo(ctx, contractAddress, p, &errACK) +// require.NoError(t, err) +// require.Equal(t, ShouldBeWritten, st.Get(ShouldBeWrittenKey("sudoerror"))) +// require.Equal(t, uint64(3050), ctx.GasMeter().GasConsumed()) +//} diff --git a/x/contractmanager/keeper/failure.go b/x/contractmanager/keeper/failure.go index 581b4f19c..038227591 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); 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); 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 ack; 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 aa00b4c91..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,28 +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().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) @@ -150,14 +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().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) @@ -166,7 +175,9 @@ 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().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgErr).Return([]byte{}, nil) @@ -180,14 +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().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) @@ -196,7 +209,9 @@ 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().Sudo(gomock.AssignableToTypeOf(ctx), contractAddr, msgTimeout).Return([]byte{}, nil) @@ -210,25 +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().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 1b33639cb..4c5a24402 100644 --- a/x/contractmanager/keeper/sudo.go +++ b/x/contractmanager/keeper/sudo.go @@ -20,97 +20,37 @@ func (k Keeper) HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) return k.wasmKeeper.HasContractInfo(ctx, contractAddress) } -func prepareSudoCallbackMessage(request channeltypes.Packet, ack *channeltypes.Acknowledgement) types.MessageSudoCallback { - m := types.MessageSudoCallback{ - Response: nil, - Error: nil, - Timeout: 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, + } + } else if ack != nil { + m.Error = &types.ErrorSudoPayload{ + Request: request, + Details: ack.GetError(), + } + } else { + m.Timeout = &types.TimeoutPayload{Request: request} } - return m -} - -func (k Keeper) SudoResponse( - ctx sdk.Context, - senderAddress sdk.AccAddress, - request channeltypes.Packet, - ack channeltypes.Acknowledgement, -) ([]byte, error) { - k.Logger(ctx).Debug("SudoResponse", "senderAddress", senderAddress, "request", request, "msg", ack.GetResult()) - - x := types.MessageResponse{} - x.Response.Data = ack.GetResult() - 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) + data, err := json.Marshal(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 nil, fmt.Errorf("failed to marshal MessageSudoCallback: %v", err) } - - return resp, nil + return data, 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) - - 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) - } - - k.Logger(ctx).Info("SudoTimeout sending request", "data", string(m)) - - resp, err := k.wasmKeeper.Sudo(ctx, senderAddress, 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) +func PrepareOpenAckCallbackMessage(details types.OpenAckDetails) ([]byte, error) { + x := types.MessageOnChanOpenAck{ + OpenAck: details, } - - return resp, nil -} - -func (k Keeper) SudoError( - ctx sdk.Context, - senderAddress sdk.AccAddress, - request channeltypes.Packet, - ack channeltypes.Acknowledgement, -) ([]byte, error) { - k.Logger(ctx).Debug("SudoError", "senderAddress", senderAddress, "request", request) - - x := types.MessageError{} - x.Error.Request = request - x.Error.Details = ack.GetError() 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 nil, fmt.Errorf("failed to marshal MessageOnChanOpenAck: %v", err) } - - return resp, nil + return m, nil } func (k Keeper) SudoOnChanOpenAck( diff --git a/x/contractmanager/keeper/sudo_test.go b/x/contractmanager/keeper/sudo_test.go index 8767acd93..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,115 +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{} - a := channeltypes.Acknowledgement{Response: &channeltypes.Acknowledgement_Result{Result: []byte("data")}} - sudoErrorMsg.Response.Data = a.GetResult() - sudoErrorMsg.Response.Request = p - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoErrorMsg)).Return([]byte("success"), nil) - resp, err := k.SudoResponse(ctx, address, sudoErrorMsg.Response.Request, a) - 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")) - resp, err = k.SudoResponse(ctx, address, sudoErrorMsg.Response.Request, a) - require.Nil(t, resp) - require.ErrorContains(t, err, "internal contract error") -} - -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{} - a := channeltypes.Acknowledgement{Response: &channeltypes.Acknowledgement_Error{ - Error: "details", - }} - sudoErrorMsg.Error.Details = a.GetError() - sudoErrorMsg.Error.Request = p - wk.EXPECT().Sudo(gomock.Any(), address, mustJSON(sudoErrorMsg)).Return([]byte("success"), nil) - resp, err := k.SudoError(ctx, address, sudoErrorMsg.Error.Request, a) - 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")) - resp, err = k.SudoError(ctx, address, sudoErrorMsg.Error.Request, a) - require.Nil(t, resp) - require.ErrorContains(t, err, "internal contract error") -} - -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) - 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")) - resp, err = k.SudoTimeout(ctx, address, sudoTimeoutMsg.Timeout.Request) - require.Nil(t, resp) - require.ErrorContains(t, err, "internal contract error") -} - -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) - 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")) - resp, err = k.SudoOnChanOpenAck(ctx, address, sudoOpenAckMsg.OpenAck) - require.Nil(t, resp) - require.ErrorContains(t, err, "internal contract error") -} - func TestSudoTxQueryResult(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() 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/module.go b/x/contractmanager/types/module.go index 540b2afd3..e7efcf2d9 100644 --- a/x/contractmanager/types/module.go +++ b/x/contractmanager/types/module.go @@ -2,15 +2,8 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) -type ContractManagerWrapper 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, ack channeltypes.Acknowledgement) ([]byte, error) - SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) - SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) - SudoOnChanOpenAck(ctx sdk.Context, contractAddress sdk.AccAddress, details OpenAckDetails) ([]byte, error) - GetParams(ctx sdk.Context) (params Params) +type Sudo interface { + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) } diff --git a/x/contractmanager/types/sudo.go b/x/contractmanager/types/sudo.go index 8b2d0144d..a5745fcbb 100644 --- a/x/contractmanager/types/sudo.go +++ b/x/contractmanager/types/sudo.go @@ -23,10 +23,12 @@ type MessageKVQueryResult struct { } `json:"kv_query_result"` } +// MessageSudoCallback is passed to a contract's sudo() entrypoint when an interchain +// transaction failed with a timeout. type MessageSudoCallback struct { - Response *ResponseSudoPayload `json:"response"` - Error *ErrorSudoPayload `json:"error"` - Timeout *TimeoutPayload `json:"timeout"` + Response *ResponseSudoPayload `json:"response,omitempty"` + Error *ErrorSudoPayload `json:"error,omitempty"` + Timeout *TimeoutPayload `json:"timeout,omitempty"` } type ResponseSudoPayload struct { @@ -43,32 +45,6 @@ type TimeoutPayload struct { Request channeltypes.Packet `json:"request"` } -// 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"` -} - -// 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"` -} - -// 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"` -} - // MessageOnChanOpenAck is passed to a contract's sudo() entrypoint when an interchain // account was successfully registered. type MessageOnChanOpenAck struct { diff --git a/x/interchaintxs/keeper/ibc_handlers.go b/x/interchaintxs/keeper/ibc_handlers.go index 4fe930e1f..1540a8c9a 100644 --- a/x/interchaintxs/keeper/ibc_handlers.go +++ b/x/interchaintxs/keeper/ibc_handlers.go @@ -1,6 +1,7 @@ package keeper import ( + "github.com/neutron-org/neutron/x/contractmanager/keeper" "time" "cosmossdk.io/errors" @@ -18,7 +19,6 @@ import ( // 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 { @@ -31,24 +31,21 @@ 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) } - - if !k.contractManagerKeeper.HasContractInfo(ctx, icaOwner.GetContract()) { - //return fmt.Errorf("%s is not a contract address", icaOwner.GetContract()) - return nil + 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)) // Actually we have only one kind of error returned from acknowledgement // maybe later we'll retrieve actual errors from events - // `err` value from `SudoError/SudoResponse` should always be nil, since we contractmanager wrapped by `SudoLimitWrapper` - if ack.GetError() != "" { - _, err = k.contractManagerKeeper.SudoError(ctx, icaOwner.GetContract(), packet, ack) - } else { - _, err = k.contractManagerKeeper.SudoResponse(ctx, icaOwner.GetContract(), packet, ack) + _, err = k.sudoKeeper.Sudo(ctx, icaOwner.GetContract(), msg) + if err != nil { + k.Logger(ctx).Debug("HandleAcknowledgement: failed to Sudo contract on packet acknowledgement", "error", err) } - return err + return nil } // HandleTimeout passes the timeout data to the appropriate contract via a sudo call. @@ -56,24 +53,25 @@ func (k *Keeper) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.Pack func (k *Keeper) HandleTimeout(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), LabelHandleTimeout) 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") } - if !k.contractManagerKeeper.HasContractInfo(ctx, icaOwner.GetContract()) { - //return fmt.Errorf("%s is not a contract address", icaOwner.GetContract()) - return nil + 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)) - // `err` value from `SudoTimeout` should always be nil, since we contractmanager wrapped by `SudoLimitWrapper` - _, err = k.contractManagerKeeper.SudoTimeout(ctx, icaOwner.GetContract(), packet) + _, err = k.sudoKeeper.Sudo(ctx, icaOwner.GetContract(), msg) + if err != nil { + k.Logger(ctx).Debug("HandleTimeout: failed to Sudo contract on packet timeout", "error", err) + } - return err + return nil } // HandleChanOpenAck passes the data about a successfully created channel to the appropriate contract @@ -96,20 +94,16 @@ func (k *Keeper) HandleChanOpenAck( return errors.Wrap(err, "failed to get ica owner from port") } - if !k.contractManagerKeeper.HasContractInfo(ctx, icaOwner.GetContract()) { - //return fmt.Errorf("%s is not a contract address", icaOwner.GetContract()) - return nil - } - - _, 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 50e74b2f0..3785f7906 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" @@ -21,19 +22,11 @@ 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, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil) + icak, infCtx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, feeKeeper, icaKeeper, nil) 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")}, } @@ -54,51 +47,30 @@ func TestHandleAcknowledgement(t *testing.T) { err = icak.HandleAcknowledgement(ctx, p, nil, relayerAddress) require.ErrorContains(t, err, "cannot unmarshal ICS-27 packet acknowledgement") - // success contract SudoResponse - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().SudoResponse(ctx, contractAddress, p, resACK) - err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) + msgAck, err := keeper.PrepareSudoCallbackMessage(p, &resACK) require.NoError(t, err) - // success contract SudoError + // success contract SudoResponse ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().SudoError(ctx, contractAddress, p, errACK) - err = icak.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck) + err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) // error contract SudoResponse ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().SudoResponse(ctx, contractAddress, p, resACK).Return(nil, fmt.Errorf("error sudoResponse")) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck).Return(nil, fmt.Errorf("error sudoResponse")) err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) - require.Error(t, err) - - // error contract SudoError - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().SudoError(ctx, contractAddress, p, errACK).Return(nil, fmt.Errorf("error sudoError")) - err = icak.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.Error(t, err) - - // no contract SudoError - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) - err = icak.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) require.NoError(t, err) //// 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) { + //wmKeeper.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}) + //wmKeeper.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) @@ -107,13 +79,13 @@ func TestHandleAcknowledgement(t *testing.T) { // //// 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) { + //wmKeeper.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) + //wmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 7000}) + //wmKeeper.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) @@ -122,11 +94,11 @@ func TestHandleAcknowledgement(t *testing.T) { // //// success 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) { + //wmKeeper.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}) + //wmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 8000}) //feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) //err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) //require.NoError(t, err) @@ -136,13 +108,13 @@ func TestHandleAcknowledgement(t *testing.T) { //// 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) { + //wmKeeper.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}) + //wmKeeper.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 } @@ -150,9 +122,9 @@ 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, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil) + icak, infCtx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, feeKeeper, icaKeeper, nil) ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) relayerBech32 := "neutron1fxudpred77a0grgh69u0j7y84yks5ev4n5050z45kecz792jnd6scqu98z" @@ -163,37 +135,33 @@ func TestHandleTimeout(t *testing.T) { SourceChannel: "channel-0", } - err := icak.HandleTimeout(ctx, channeltypes.Packet{}, relayerAddress) + msgAck, err := keeper.PrepareSudoCallbackMessage(p, nil) + require.NoError(t, err) + + 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().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().SudoTimeout(ctx, contractAddress, p) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck) err = icak.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) // contract error ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().SudoTimeout(ctx, contractAddress, p).Return(nil, fmt.Errorf("SudoTimeout error")) - err = icak.HandleTimeout(ctx, p, relayerAddress) - require.Error(t, err) - - // no 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("SudoTimeout error")) err = icak.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) + } 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) + icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, nil, nil, nil) portID := icatypes.ControllerPortPrefix + testutil.TestOwnerAddress + ".ica0" contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) channelID := "channel-0" @@ -202,27 +170,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().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - 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")) - err = icak.HandleChanOpenAck(ctx, portID, channelID, counterpartyChannelID, "1") - require.ErrorContains(t, err, "failed to sudo the contract OnChanOpenAck") + }) + require.NoError(t, err) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - cmKeeper.EXPECT().SudoOnChanOpenAck(ctx, contractAddress, types.OpenAckDetails{ - PortID: portID, - ChannelID: channelID, - CounterpartyChannelID: counterpartyChannelID, - CounterpartyVersion: "1", - }).Return(nil, nil) + // sudo error + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msg).Return(nil, fmt.Errorf("SudoOnChanOpenAck error")) err = icak.HandleChanOpenAck(ctx, portID, channelID, counterpartyChannelID, "1") require.NoError(t, err) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) + // 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..737a874b5 100644 --- a/x/interchaintxs/keeper/keeper.go +++ b/x/interchaintxs/keeper/keeper.go @@ -20,13 +20,13 @@ 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 } ) @@ -36,17 +36,17 @@ func NewKeeper( memKey storetypes.StoreKey, channelKeeper types.ChannelKeeper, icaControllerKeeper types.ICAControllerKeeper, - contractManagerKeeper types.ContractManagerKeeper, + contractManagerKeeper types.WasmKeeper, feeKeeper types.FeeRefunderKeeper, ) *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: contractManagerKeeper, + feeKeeper: feeKeeper, } } diff --git a/x/interchaintxs/keeper/msg_server.go b/x/interchaintxs/keeper/msg_server.go index 62d3cfe5a..efc3a42d9 100644 --- a/x/interchaintxs/keeper/msg_server.go +++ b/x/interchaintxs/keeper/msg_server.go @@ -43,7 +43,7 @@ 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) } @@ -79,7 +79,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..c662dd8da 100644 --- a/x/interchaintxs/keeper/msg_server_test.go +++ b/x/interchaintxs/keeper/msg_server_test.go @@ -25,8 +25,8 @@ 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) + icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, nil, icaKeeper, nil) goCtx := sdk.WrapSDKContext(ctx) msgRegAcc := types.MsgRegisterInterchainAccount{ @@ -41,18 +41,18 @@ 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) 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) icaKeeper.EXPECT().RegisterInterchainAccount(ctx, msgRegAcc.ConnectionId, icaOwner.String(), "").Return(nil) resp, err = icak.RegisterInterchainAccount(goCtx, &msgRegAcc) require.NoError(t, err) @@ -63,10 +63,10 @@ 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) + icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, refundKeeper, icaKeeper, channelKeeper) goCtx := sdk.WrapSDKContext(ctx) cosmosMsg := codectypes.Any{ @@ -97,7 +97,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 +105,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 +128,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 +136,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 +153,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 +162,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/types/expected_keepers.go b/x/interchaintxs/types/expected_keepers.go index 13d7f7cf3..3e57253ed 100644 --- a/x/interchaintxs/types/expected_keepers.go +++ b/x/interchaintxs/types/expected_keepers.go @@ -8,7 +8,6 @@ import ( channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" - contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" feerefundertypes "github.com/neutron-org/neutron/x/feerefunder/types" ) @@ -24,14 +23,9 @@ type BankKeeper interface { // Methods imported from bank should be defined here } -type ContractManagerKeeper interface { +type WasmKeeper interface { HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool - SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) - SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]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) - AddContractFailure(ctx sdk.Context, packet *channeltypes.Packet, address, ackType string, ack *channeltypes.Acknowledgement) - GetParams(ctx sdk.Context) (params contractmanagertypes.Params) + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) } type ICAControllerKeeper interface { diff --git a/x/transfer/ibc_handlers.go b/x/transfer/ibc_handlers.go index efecb0cff..30ad94af3 100644 --- a/x/transfer/ibc_handlers.go +++ b/x/transfer/ibc_handlers.go @@ -6,6 +6,7 @@ import ( 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/x/contractmanager/keeper" feetypes "github.com/neutron-org/neutron/x/feerefunder/types" "github.com/neutron-org/neutron/x/interchaintxs/types" ) @@ -25,24 +26,25 @@ 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)) - if ack.Success() { - _, err = im.ContractManagerKeeper.SudoResponse(ctx, senderAddress, packet, ack) - } else { - // Actually we have only one kind of error returned from acknowledgement - // maybe later we'll retrieve actual errors from events - im.keeper.Logger(ctx).Debug(ack.GetError(), "CheckTx", ctx.IsCheckTx()) - _, err = im.ContractManagerKeeper.SudoError(ctx, senderAddress, packet, ack) + msg, err := keeper.PrepareSudoCallbackMessage(packet, &ack) + if err != nil { + return errors.Wrapf(sdkerrors.ErrJSONMarshal, "failed to marshal Packet/Acknowledgment: %v", err) + } + + _, 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 err + return nil } // HandleTimeout passes the timeout data to the appropriate contract via a sudo call. @@ -56,13 +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)) - _, err = im.ContractManagerKeeper.SudoTimeout(ctx, senderAddress, packet) + _, 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) + } - return err + return nil } diff --git a/x/transfer/ibc_handlers_test.go b/x/transfer/ibc_handlers_test.go index e93d91c97..03ebab6f7 100644 --- a/x/transfer/ibc_handlers_test.go +++ b/x/transfer/ibc_handlers_test.go @@ -2,6 +2,7 @@ package transfer_test import ( "fmt" + "github.com/neutron-org/neutron/x/contractmanager/keeper" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -19,36 +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, _ := 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)) - 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")}, } @@ -59,6 +43,7 @@ func TestHandleAcknowledgement(t *testing.T) { SourcePort: "transfer", SourceChannel: "channel-0", } + contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) relayerBech32 := "neutron1fxudpred77a0grgh69u0j7y84yks5ev4n5050z45kecz792jnd6scqu98z" relayerAddress := sdk.MustAccAddressFromBech32(relayerBech32) @@ -92,41 +77,28 @@ func TestHandleAcknowledgement(t *testing.T) { require.NoError(t, err) p.Data = tokenBz + msgAck, err := keeper.PrepareSudoCallbackMessage(p, &resACK) + require.NoError(t, err) + // 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.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) - // error during SudoResponse contract + // error during Sudo contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - 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)) - cmKeeper.EXPECT().SudoResponse(ctx, contractAddress, p, resACK).Return(nil, fmt.Errorf("SudoResponse error")) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck).Return(nil, fmt.Errorf("SudoResponse error")) err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) - require.Error(t, err) - - // error during SudoError contract - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().SudoError(ctx, contractAddress, p, errACK).Return(nil, fmt.Errorf("SudoError error")) - err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) - require.Error(t, err) - - // success during SudoError - ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) - feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - cmKeeper.EXPECT().SudoError(ctx, contractAddress, p, errACK) - err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) require.NoError(t, err) - // success during SudoError contract + // success during Sudo contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - 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)) - cmKeeper.EXPECT().SudoResponse(ctx, contractAddress, p, resACK) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck) err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) } @@ -134,14 +106,14 @@ func TestHandleAcknowledgement(t *testing.T) { 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, _ := 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)) contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) relayerBech32 := "neutron1fxudpred77a0grgh69u0j7y84yks5ev4n5050z45kecz792jnd6scqu98z" @@ -177,25 +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.NoError(t, err) // success contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - 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)) - cmKeeper.EXPECT().SudoTimeout(ctx, contractAddress, p).Return(nil, nil) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msg).Return(nil, nil) err = txModule.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) // error during SudoTimeOut contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - 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)) - cmKeeper.EXPECT().SudoTimeout(ctx, contractAddress, p).Return(nil, fmt.Errorf("SudoTimeout error")) + wmKeeper.EXPECT().Sudo(ctx, contractAddress, msg).Return(nil, fmt.Errorf("SudoTimeout error")) err = txModule.HandleTimeout(ctx, p, relayerAddress) - require.Error(t, err) + require.NoError(t, err) } 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 774daf841..854b8e490 100644 --- a/x/transfer/types/expected_keepers.go +++ b/x/transfer/types/expected_keepers.go @@ -7,12 +7,9 @@ import ( 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 - SudoResponse(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) - SudoError(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, ack channeltypes.Acknowledgement) ([]byte, error) - SudoTimeout(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) ([]byte, error) + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) } type FeeRefunderKeeper interface { From 7db0608a02672c38b79f791902a59123b3b22515 Mon Sep 17 00:00:00 2001 From: swelf Date: Thu, 14 Sep 2023 19:51:50 +0300 Subject: [PATCH 3/7] added fee charging for ica creation --- app/app.go | 2 + app/upgrades/sdk47/upgrades.go | 3 +- proto/neutron/interchaintxs/v1/params.proto | 3 + proto/neutron/interchaintxs/v1/tx.proto | 5 + .../interchaintxs/keeper/interchaintxs.go | 13 +- .../interchaintxs/types/expected_keepers.go | 60 ++++++- wasmbinding/bindings/msg.go | 6 +- wasmbinding/message_plugin.go | 1 + wasmbinding/test/custom_message_test.go | 13 ++ x/interchaintxs/genesis_test.go | 2 +- .../grpc_query_interchainaccount_test.go | 2 +- .../keeper/grpc_query_params_test.go | 2 +- x/interchaintxs/keeper/ibc_handlers_test.go | 12 +- x/interchaintxs/keeper/keeper.go | 30 ++++ x/interchaintxs/keeper/msg_server.go | 4 + x/interchaintxs/keeper/msg_server_test.go | 38 ++++- x/interchaintxs/keeper/params_test.go | 2 +- x/interchaintxs/types/expected_keepers.go | 7 +- x/interchaintxs/types/params.go | 21 +-- x/interchaintxs/types/params.pb.go | 98 +++++++++-- x/interchaintxs/types/tx.pb.go | 158 ++++++++++++------ 21 files changed, 386 insertions(+), 96 deletions(-) diff --git a/app/app.go b/app/app.go index 6804471df..5a5e71871 100644 --- a/app/app.go +++ b/app/app.go @@ -662,6 +662,8 @@ func New( app.ICAControllerKeeper, 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) 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/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 4961a46f9..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.WasmKeeper, 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.WasmKeeper, refunderK 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.WasmKeeper, refunderK err := k.SetParams(ctx, types.DefaultParams()) require.NoError(t, err) - return k, ctx, storeKey + return k, ctx } diff --git a/testutil/mocks/interchaintxs/types/expected_keepers.go b/testutil/mocks/interchaintxs/types/expected_keepers.go index d42f778a0..97633afb9 100644 --- a/testutil/mocks/interchaintxs/types/expected_keepers.go +++ b/testutil/mocks/interchaintxs/types/expected_keepers.go @@ -14,7 +14,8 @@ 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/feerefunder/types" + types4 "github.com/neutron-org/neutron/x/feeburner/types" + types5 "github.com/neutron-org/neutron/x/feerefunder/types" ) // MockAccountKeeper is a mock of AccountKeeper interface. @@ -77,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() @@ -249,7 +264,7 @@ func (m *MockFeeRefunderKeeper) EXPECT() *MockFeeRefunderKeeperMockRecorder { } // DistributeAcknowledgementFee mocks base method. -func (m *MockFeeRefunderKeeper) DistributeAcknowledgementFee(ctx types.Context, receiver types.AccAddress, packetID types4.PacketID) { +func (m *MockFeeRefunderKeeper) DistributeAcknowledgementFee(ctx types.Context, receiver types.AccAddress, packetID types5.PacketID) { m.ctrl.T.Helper() m.ctrl.Call(m, "DistributeAcknowledgementFee", ctx, receiver, packetID) } @@ -261,7 +276,7 @@ func (mr *MockFeeRefunderKeeperMockRecorder) DistributeAcknowledgementFee(ctx, r } // DistributeTimeoutFee mocks base method. -func (m *MockFeeRefunderKeeper) DistributeTimeoutFee(ctx types.Context, receiver types.AccAddress, packetID types4.PacketID) { +func (m *MockFeeRefunderKeeper) DistributeTimeoutFee(ctx types.Context, receiver types.AccAddress, packetID types5.PacketID) { m.ctrl.T.Helper() m.ctrl.Call(m, "DistributeTimeoutFee", ctx, receiver, packetID) } @@ -273,7 +288,7 @@ func (mr *MockFeeRefunderKeeperMockRecorder) DistributeTimeoutFee(ctx, receiver, } // LockFees mocks base method. -func (m *MockFeeRefunderKeeper) LockFees(ctx types.Context, payer types.AccAddress, packetID types4.PacketID, fee types4.Fee) error { +func (m *MockFeeRefunderKeeper) LockFees(ctx types.Context, payer types.AccAddress, packetID types5.PacketID, fee types5.Fee) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockFees", ctx, payer, packetID, fee) ret0, _ := ret[0].(error) @@ -353,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/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 1f5d11d5b..5346823d7 100644 --- a/wasmbinding/test/custom_message_test.go +++ b/wasmbinding/test/custom_message_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" keeper2 "github.com/neutron-org/neutron/x/contractmanager/keeper" + feeburnertypes "github.com/neutron-org/neutron/x/feeburner/types" "testing" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" @@ -76,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, 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/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_test.go b/x/interchaintxs/keeper/ibc_handlers_test.go index 3785f7906..50cc29fd8 100644 --- a/x/interchaintxs/keeper/ibc_handlers_test.go +++ b/x/interchaintxs/keeper/ibc_handlers_test.go @@ -24,7 +24,9 @@ func TestHandleAcknowledgement(t *testing.T) { icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) wmKeeper := mock_types.NewMockWasmKeeper(ctrl) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) - icak, infCtx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, 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)) resACK := channeltypes.Acknowledgement{ @@ -124,7 +126,9 @@ func TestHandleTimeout(t *testing.T) { icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) wmKeeper := mock_types.NewMockWasmKeeper(ctrl) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) - icak, infCtx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, 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)) contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) relayerBech32 := "neutron1fxudpred77a0grgh69u0j7y84yks5ev4n5050z45kecz792jnd6scqu98z" @@ -161,7 +165,9 @@ func TestHandleChanOpenAck(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() wmKeeper := mock_types.NewMockWasmKeeper(ctrl) - icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, nil, nil, nil) + 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" diff --git a/x/interchaintxs/keeper/keeper.go b/x/interchaintxs/keeper/keeper.go index 737a874b5..e27d294d2 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" @@ -27,6 +29,8 @@ type ( feeKeeper types.FeeRefunderKeeper icaControllerKeeper types.ICAControllerKeeper sudoKeeper types.WasmKeeper + bankKeeper types.BankKeeper + feeBurnerKeeper types.FeeBurnerKeeper } ) @@ -38,6 +42,8 @@ func NewKeeper( icaControllerKeeper types.ICAControllerKeeper, contractManagerKeeper types.WasmKeeper, feeKeeper types.FeeRefunderKeeper, + bankKeeper types.BankKeeper, + feeBurnerKeeper types.FeeBurnerKeeper, ) *Keeper { return &Keeper{ Codec: cdc, @@ -47,9 +53,33 @@ func NewKeeper( icaControllerKeeper: icaControllerKeeper, sudoKeeper: contractManagerKeeper, 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: %v < %v", 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: %v: %v", treasury, err) + } + + err = k.bankKeeper.SendCoins(ctx, payer, treasuryAddress, fee) + if err != nil { + return errors.Wrapf(err, "failed send fee(%v) from %v to %v", fee, payer, treasury) + } + return nil +} diff --git a/x/interchaintxs/keeper/msg_server.go b/x/interchaintxs/keeper/msg_server.go index efc3a42d9..4c23bfbef 100644 --- a/x/interchaintxs/keeper/msg_server.go +++ b/x/interchaintxs/keeper/msg_server.go @@ -48,6 +48,10 @@ func (k Keeper) RegisterInterchainAccount(goCtx context.Context, msg *ictxtypes. 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 diff --git a/x/interchaintxs/keeper/msg_server_test.go b/x/interchaintxs/keeper/msg_server_test.go index c662dd8da..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) wmKeeper := mock_types.NewMockWasmKeeper(ctrl) - icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, nil, icaKeeper, nil) + 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{ @@ -47,12 +53,38 @@ func TestRegisterInterchainAccount(t *testing.T) { require.Nil(t, resp) 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) 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) @@ -66,7 +98,9 @@ func TestSubmitTx(t *testing.T) { wmKeeper := mock_types.NewMockWasmKeeper(ctrl) refundKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) channelKeeper := mock_types.NewMockChannelKeeper(ctrl) - icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, wmKeeper, 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{ 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 3e57253ed..23648d88d 100644 --- a/x/interchaintxs/types/expected_keepers.go +++ b/x/interchaintxs/types/expected_keepers.go @@ -7,6 +7,7 @@ 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" feerefundertypes "github.com/neutron-org/neutron/x/feerefunder/types" ) @@ -20,7 +21,7 @@ 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 WasmKeeper interface { @@ -47,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 } From 70c599554f42beac36f4b49f6b8c167c82349699 Mon Sep 17 00:00:00 2001 From: swelf Date: Fri, 15 Sep 2023 16:49:36 +0300 Subject: [PATCH 4/7] review fixes: extra tests, removed commented/dead code --- Makefile | 2 +- neutronutils/errors/errors.go | 21 --- neutronutils/utils.go | 11 -- .../interchaintxs/keeper/sudo_middleware.go | 27 ++++ .../contractmanager/types/expected_keepers.go | 50 ++++++ x/contractmanager/ibc_middleware.go | 35 +++- x/contractmanager/ibc_middleware_test.go | 153 ++++++++---------- x/contractmanager/types/expected_keepers.go | 5 + x/interchaintxs/handler.go | 34 ---- x/interchaintxs/keeper/ibc_handlers_test.go | 53 ------ 10 files changed, 182 insertions(+), 209 deletions(-) delete mode 100644 neutronutils/errors/errors.go delete mode 100644 neutronutils/utils.go create mode 100644 testutil/interchaintxs/keeper/sudo_middleware.go delete mode 100644 x/interchaintxs/handler.go diff --git a/Makefile b/Makefile index 1bdcceca1..6af490db8 100644 --- a/Makefile +++ b/Makefile @@ -123,7 +123,7 @@ build-static-linux-amd64: go.sum $(BUILDDIR)/ $(DOCKER) cp neutronbinary:/bin/neutrond $(BUILDDIR)/neutrond-linux-amd64 $(DOCKER) rm -f neutronbinary -install-test-binary: go.sum +install-test-binary: check_version go.sum go install -mod=readonly $(BUILD_FLAGS_TEST_BINARY) ./cmd/neutrond ######################################## 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/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/x/contractmanager/ibc_middleware.go b/x/contractmanager/ibc_middleware.go index 841897459..739de0860 100644 --- a/x/contractmanager/ibc_middleware.go +++ b/x/contractmanager/ibc_middleware.go @@ -1,22 +1,20 @@ package contractmanager import ( + "cosmossdk.io/errors" "fmt" "github.com/cometbft/cometbft/libs/log" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/neutron-org/neutron/neutronutils" - neutronerrors "github.com/neutron-org/neutron/neutronutils/errors" - "github.com/neutron-org/neutron/x/contractmanager/keeper" contractmanagertypes "github.com/neutron-org/neutron/x/contractmanager/types" ) type SudoLimitWrapper struct { - contractManager keeper.Keeper + contractManager contractmanagertypes.ContractManagerKeeper contractmanagertypes.WasmKeeper } // NewSudoLimitWrapper suppresses an error from a Sudo contract handler and saves it to a store -func NewSudoLimitWrapper(contractManager keeper.Keeper, sudoKeeper contractmanagertypes.WasmKeeper) contractmanagertypes.WasmKeeper { +func NewSudoLimitWrapper(contractManager contractmanagertypes.ContractManagerKeeper, sudoKeeper contractmanagertypes.WasmKeeper) contractmanagertypes.WasmKeeper { return SudoLimitWrapper{ contractManager, sudoKeeper, @@ -27,9 +25,9 @@ func NewSudoLimitWrapper(contractManager keeper.Keeper, sudoKeeper contractmanag // 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 := neutronutils.CreateCachedContext(ctx, k.contractManager.GetParams(ctx).SudoCallGasLimit) + cacheCtx, writeFn := createCachedContext(ctx, k.contractManager.GetParams(ctx).SudoCallGasLimit) func() { - defer neutronerrors.OutOfGasRecovery(cacheCtx.GasMeter(), &err) + 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) @@ -49,3 +47,26 @@ func (k SudoLimitWrapper) Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, 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 index b1b964fbf..0815be992 100644 --- a/x/contractmanager/ibc_middleware_test.go +++ b/x/contractmanager/ibc_middleware_test.go @@ -1,83 +1,72 @@ -package contractmanager +package contractmanager_test -// -//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" -// 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" -// "github.com/golang/mock/gomock" -// mock_types "github.com/neutron-org/neutron/testutil/mocks/interchaintxs/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 NewSudoLimitMiddleware(t testing.TB, cm types.SudoWrapper) (SudoLimitWrapper, 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()) -// -// k := SudoLimitWrapper{SudoWrapper: cm} -// -// ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) -// -// return k, ctx, storeKey -//} -// -//func TestSudo(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) -// middleware, infCtx, storeKey := NewSudoLimitMiddleware(t, cmKeeper) -// st := infCtx.KVStore(storeKey) -// -// p := channeltypes.Packet{ -// Sequence: 100, -// SourcePort: icatypes.ControllerPortPrefix + TestOwnerAddress + ".ica0", -// SourceChannel: "channel-0", -// } -// contractAddress := sdk.AccAddress{} -// 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")}, -// //} -// //resAckData, err := channeltypes.SubModuleCdc.MarshalJSON(&resACK) -// //require.NoError(t, err) -// -// // success during SudoError -// ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) -// cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, errAck channeltypes.Acknowledgement) { -// st := cachedCtx.KVStore(storeKey) -// st.Set(ShouldBeWrittenKey("sudoerror"), ShouldBeWritten) -// }).Return(nil, nil) -// cmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 6000}) -// err := middleware.Sudo(ctx, contractAddress, p, &errACK) -// require.NoError(t, err) -// require.Equal(t, ShouldBeWritten, st.Get(ShouldBeWrittenKey("sudoerror"))) -// require.Equal(t, uint64(3050), ctx.GasMeter().GasConsumed()) -//} +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/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/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/ibc_handlers_test.go b/x/interchaintxs/keeper/ibc_handlers_test.go index 50cc29fd8..c0a8c40b6 100644 --- a/x/interchaintxs/keeper/ibc_handlers_test.go +++ b/x/interchaintxs/keeper/ibc_handlers_test.go @@ -65,59 +65,6 @@ func TestHandleAcknowledgement(t *testing.T) { wmKeeper.EXPECT().Sudo(ctx, contractAddress, msgAck).Return(nil, fmt.Errorf("error sudoResponse")) err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) - - //// success during SudoError - //ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - //wmKeeper.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) - //wmKeeper.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)) - //wmKeeper.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") - //}) - //wmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 7000}) - //wmKeeper.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(7000), ctx.GasMeter().GasConsumed()) - // - //// success during SudoResponse - //ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) - //wmKeeper.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) - //wmKeeper.EXPECT().GetParams(ctx).Return(types.Params{SudoCallGasLimit: 8000}) - //feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - //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)) - //wmKeeper.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)) - //wmKeeper.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) { From 0c26b8e4febf36517626a221dc267fcce12e09e5 Mon Sep 17 00:00:00 2001 From: swelf Date: Fri, 15 Sep 2023 19:20:47 +0300 Subject: [PATCH 5/7] comment typo --- x/contractmanager/types/sudo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/contractmanager/types/sudo.go b/x/contractmanager/types/sudo.go index a5745fcbb..8951b7c7d 100644 --- a/x/contractmanager/types/sudo.go +++ b/x/contractmanager/types/sudo.go @@ -24,7 +24,7 @@ type MessageKVQueryResult struct { } // MessageSudoCallback is passed to a contract's sudo() entrypoint when an interchain -// transaction failed with a timeout. +// transaction failed. type MessageSudoCallback struct { Response *ResponseSudoPayload `json:"response,omitempty"` Error *ErrorSudoPayload `json:"error,omitempty"` From 3dfb8deaed971c257788d6843003bced535184a2 Mon Sep 17 00:00:00 2001 From: swelf Date: Tue, 19 Sep 2023 11:26:22 +0300 Subject: [PATCH 6/7] review fixes --- x/contractmanager/keeper/failure.go | 2 +- x/contractmanager/types/module.go | 9 --------- x/contractmanager/types/sudo.go | 2 +- x/interchaintxs/keeper/keeper.go | 10 +++++----- 4 files changed, 7 insertions(+), 16 deletions(-) delete mode 100644 x/contractmanager/types/module.go diff --git a/x/contractmanager/keeper/failure.go b/x/contractmanager/keeper/failure.go index 038227591..02e653655 100644 --- a/x/contractmanager/keeper/failure.go +++ b/x/contractmanager/keeper/failure.go @@ -73,7 +73,7 @@ func (k Keeper) ResubmitFailure(ctx sdk.Context, contractAddr sdk.AccAddress, fa } if _, err := k.wasmKeeper.Sudo(ctx, contractAddr, failure.SudoPayload); err != nil { - return errors.Wrapf(types.FailedToResubmitFailure, "cannot resubmit failure ack; failureId = %d; err = %s", failure.Id, err) + 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/types/module.go b/x/contractmanager/types/module.go deleted file mode 100644 index e7efcf2d9..000000000 --- a/x/contractmanager/types/module.go +++ /dev/null @@ -1,9 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type Sudo interface { - Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) -} diff --git a/x/contractmanager/types/sudo.go b/x/contractmanager/types/sudo.go index 8951b7c7d..0670a1696 100644 --- a/x/contractmanager/types/sudo.go +++ b/x/contractmanager/types/sudo.go @@ -24,7 +24,7 @@ type MessageKVQueryResult struct { } // MessageSudoCallback is passed to a contract's sudo() entrypoint when an interchain -// transaction failed. +// transaction ended up with Success/Error or timed out. type MessageSudoCallback struct { Response *ResponseSudoPayload `json:"response,omitempty"` Error *ErrorSudoPayload `json:"error,omitempty"` diff --git a/x/interchaintxs/keeper/keeper.go b/x/interchaintxs/keeper/keeper.go index e27d294d2..0cf14c884 100644 --- a/x/interchaintxs/keeper/keeper.go +++ b/x/interchaintxs/keeper/keeper.go @@ -40,7 +40,7 @@ func NewKeeper( memKey storetypes.StoreKey, channelKeeper types.ChannelKeeper, icaControllerKeeper types.ICAControllerKeeper, - contractManagerKeeper types.WasmKeeper, + sudoKeeper types.WasmKeeper, feeKeeper types.FeeRefunderKeeper, bankKeeper types.BankKeeper, feeBurnerKeeper types.FeeBurnerKeeper, @@ -51,7 +51,7 @@ func NewKeeper( memKey: memKey, channelKeeper: channelKeeper, icaControllerKeeper: icaControllerKeeper, - sudoKeeper: contractManagerKeeper, + sudoKeeper: sudoKeeper, feeKeeper: feeKeeper, bankKeeper: bankKeeper, feeBurnerKeeper: feeBurnerKeeper, @@ -68,18 +68,18 @@ func (k Keeper) ChargeFee(ctx sdk.Context, payer sdk.AccAddress, fee sdk.Coins) params := k.GetParams(ctx) if !fee.IsAnyGTE(params.RegisterFee) { - return errors.Wrapf(sdkerrors.ErrInsufficientFee, "provided fee is less than min governance set ack fee: %v < %v", fee, 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: %v: %v", treasury, err) + 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(%v) from %v to %v", fee, payer, treasury) + return errors.Wrapf(err, "failed send fee(%s) from %s to %s", fee, payer, treasury) } return nil } From 2331798dc85dbaa0353b364f556dd87d48afa351 Mon Sep 17 00:00:00 2001 From: swelf Date: Tue, 19 Sep 2023 12:16:58 +0300 Subject: [PATCH 7/7] removed unused method --- x/contractmanager/keeper/sudo.go | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/x/contractmanager/keeper/sudo.go b/x/contractmanager/keeper/sudo.go index 4c5a24402..13a5e9e50 100644 --- a/x/contractmanager/keeper/sudo.go +++ b/x/contractmanager/keeper/sudo.go @@ -53,33 +53,6 @@ func PrepareOpenAckCallbackMessage(details types.OpenAckDetails) ([]byte, error) return m, nil } -func (k Keeper) SudoOnChanOpenAck( - ctx sdk.Context, - contractAddress sdk.AccAddress, - details types.OpenAckDetails, -) ([]byte, error) { - k.Logger(ctx).Debug("SudoOnChanOpenAck", "contractAddress", contractAddress) - - 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 -} - // SudoTxQueryResult is used to pass a tx query result to the contract that registered the query // to: // 1. check whether the transaction actually satisfies the initial query arguments;