From 5a2b7416678ddd105aa250af83128370a7644935 Mon Sep 17 00:00:00 2001 From: swelf Date: Mon, 3 Jul 2023 16:40:07 +0300 Subject: [PATCH] added checks on state reset during sudo call failures(ica tx, transfer wapper) --- .../interchaintxs/keeper/interchaintxs.go | 4 +- testutil/transfer/keeper/keeper.go | 4 +- 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 | 85 ++++++++--- x/interchaintxs/keeper/msg_server_test.go | 4 +- x/interchaintxs/keeper/params_test.go | 2 +- x/transfer/ibc_handlers_test.go | 142 ++++++++++++++---- 9 files changed, 189 insertions(+), 58 deletions(-) diff --git a/testutil/interchaintxs/keeper/interchaintxs.go b/testutil/interchaintxs/keeper/interchaintxs.go index 42acb7246..c27b4be22 100644 --- a/testutil/interchaintxs/keeper/interchaintxs.go +++ b/testutil/interchaintxs/keeper/interchaintxs.go @@ -18,7 +18,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, capabilityKeeper types.ScopedKeeper) (*keeper.Keeper, sdk.Context) { +func InterchainTxsKeeper(t testing.TB, managerKeeper types.ContractManagerKeeper, refunderKeeper types.FeeRefunderKeeper, icaControllerKeeper types.ICAControllerKeeper, channelKeeper types.ChannelKeeper, capabilityKeeper types.ScopedKeeper) (*keeper.Keeper, sdk.Context, *sdk.KVStoreKey) { storeKey := sdk.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) @@ -54,5 +54,5 @@ func InterchainTxsKeeper(t testing.TB, managerKeeper types.ContractManagerKeeper // Initialize params k.SetParams(ctx, types.DefaultParams()) - return k, ctx + return k, ctx, storeKey } diff --git a/testutil/transfer/keeper/keeper.go b/testutil/transfer/keeper/keeper.go index c9894a58b..09650f5b6 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) { +func TransferKeeper(t testing.TB, managerKeeper types.ContractManagerKeeper, refunderKeeper types.FeeRefunderKeeper, channelKeeper types.ChannelKeeper, authKeeper types.AccountKeeper) (*keeper.KeeperTransferWrapper, sdk.Context, *sdk.KVStoreKey) { storeKey := sdk.NewKVStoreKey(transfertypes.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey("mem_" + transfertypes.StoreKey) @@ -60,5 +60,5 @@ func TransferKeeper(t testing.TB, managerKeeper types.ContractManagerKeeper, ref // Initialize params k.SetParams(ctx, transfertypes.DefaultParams()) - return &k, ctx + return &k, ctx, storeKey } diff --git a/x/interchaintxs/genesis_test.go b/x/interchaintxs/genesis_test.go index ac0503ed3..3d24817c4 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, nil) + k, ctx, _ := keepertest.InterchainTxsKeeper(t, 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 9b141d7b2..636ac0e26 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, nil) + keeper, ctx, _ := testkeeper.InterchainTxsKeeper(t, nil, nil, icaKeeper, 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 b53d7918b..a68bf6c61 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, nil) + keeper, ctx, _ := testkeeper.InterchainTxsKeeper(t, nil, nil, nil, nil, nil) wctx := sdk.WrapSDKContext(ctx) params := types.DefaultParams() keeper.SetParams(ctx, params) diff --git a/x/interchaintxs/keeper/ibc_handlers_test.go b/x/interchaintxs/keeper/ibc_handlers_test.go index 8781189c1..2b292b5d3 100644 --- a/x/interchaintxs/keeper/ibc_handlers_test.go +++ b/x/interchaintxs/keeper/ibc_handlers_test.go @@ -18,14 +18,25 @@ import ( "github.com/neutron-org/neutron/x/interchaintxs/keeper" ) +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 := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil, nil) + icak, infCtx, storeKey := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil, nil) ctx := infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) + store := ctx.KVStore(storeKey) errACK := channeltypes.Acknowledgement{ Response: &channeltypes.Acknowledgement_Error{ @@ -55,54 +66,81 @@ func TestHandleAcknowledgement(t *testing.T) { require.ErrorContains(t, err, "cannot unmarshal ICS-27 packet acknowledgement") // error during SudoResponse - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(ctx), contractAddress, p, resACK.GetResult()).Return(nil, fmt.Errorf("SudoResponse error")) + 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") 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.Empty(t, store.Get(ShouldNotBeWrittenKey)) + require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) // error during SudoError - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Return(nil, fmt.Errorf("SudoError error")) + 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) err = icak.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) require.NoError(t, err) + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) + require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) // success during SudoError - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Return(nil, nil) + 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) 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"))) // out of gas during SudoError - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, error string) { - ctx.GasMeter().ConsumeGas(ctx.GasMeter().Limit()+1, "out of gas test") + 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(cachedCtx.GasMeter().Limit()+1, "out of gas test") }).Return(nil, fmt.Errorf("SudoError error")) cmKeeper.EXPECT().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") 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(0), ctx.GasMeter().GasConsumed()) // due to out of gas recovery we consume 0 with a SudoError handler // 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 + 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 ctx.GasMeter().Limit() == cachedCtx.GasMeter().Limit()+keeper.GasReserve { gasReserved = true } - cachedCtx.GasMeter().ConsumeGas(1_000_000, "Sudo response consumption") + store := cachedCtx.KVStore(storeKey) + store.Set(ShouldBeWrittenKey("sudoresponse"), ShouldBeWritten) // consumes 3140 gas, 2000 flat write + 30 every byte of key+value }).Return(nil, nil) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - consumedBefore := ctx.GasMeter().GasConsumed() err = icak.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) require.True(t, gasReserved) - require.Equal(t, consumedBefore+1_000_000, ctx.GasMeter().GasConsumed()) + require.Equal(t, uint64(3140), ctx.GasMeter().GasConsumed()) + require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudoresponse"))) // not enough gas to reserve + not enough to make AddContractFailure failure after panic recover + ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) lowGasCtx := infCtx.WithGasMeter(sdk.NewGasMeter(keeper.GasReserve - 1)) 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(1, "Sudo response consumption") }).Return(nil, nil) feeKeeper.EXPECT().DistributeAcknowledgementFee(lowGasCtx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) @@ -110,6 +148,7 @@ func TestHandleAcknowledgement(t *testing.T) { ctx.GasMeter().ConsumeGas(keeper.GasReserve, "out of gas") }) require.Panics(t, func() { icak.HandleAcknowledgement(lowGasCtx, p, resAckData, relayerAddress) }) //nolint:errcheck // this is a panic test + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) } func TestHandleTimeout(t *testing.T) { @@ -118,8 +157,9 @@ func TestHandleTimeout(t *testing.T) { icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) feeKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) - icak, infCtx := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil, nil) + icak, infCtx, storeKey := testkeeper.InterchainTxsKeeper(t, cmKeeper, feeKeeper, icaKeeper, nil, 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) @@ -133,41 +173,52 @@ func TestHandleTimeout(t *testing.T) { require.ErrorContains(t, err, "failed to get ica owner from port") gasReserved := false + ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(cachedCtx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { if ctx.GasMeter().Limit() == cachedCtx.GasMeter().Limit()+keeper.GasReserve { gasReserved = true } - cachedCtx.GasMeter().ConsumeGas(1_000_000, "Sudo timeout consumption") + store := cachedCtx.KVStore(storeKey) + store.Set(ShouldBeWrittenKey("sudotimeout"), ShouldBeWritten) // consumes 3110 gas, 2000 flat write + 30 every byte of key+value }).Return(nil, nil) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - consumedBefore := ctx.GasMeter().GasConsumed() err = icak.HandleTimeout(ctx, p, relayerAddress) require.True(t, gasReserved) - require.Equal(t, consumedBefore+1_000_000, ctx.GasMeter().GasConsumed()) + require.Equal(t, uint64(3110), ctx.GasMeter().GasConsumed()) + require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudotimeout"))) require.NoError(t, err) // error during SudoTimeOut - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Return(nil, fmt.Errorf("SudoTimeout 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "timeout") feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) err = icak.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) // out of gas during SudoTimeOut - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - ctx.GasMeter().ConsumeGas(ctx.GasMeter().Limit()+1, "out of gas test") + 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(cachedCtx.GasMeter().Limit()+1, "out of gas test") }).Return(nil, fmt.Errorf("SudoTimeout error")) cmKeeper.EXPECT().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "timeout") feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) err = icak.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) } 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, nil) + icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, nil, nil, nil, nil) portID := icatypes.PortPrefix + testutil.TestOwnerAddress + ".ica0" contractAddress := sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress) channelID := "channel-0" diff --git a/x/interchaintxs/keeper/msg_server_test.go b/x/interchaintxs/keeper/msg_server_test.go index e28b1e5f2..f3e545f45 100644 --- a/x/interchaintxs/keeper/msg_server_test.go +++ b/x/interchaintxs/keeper/msg_server_test.go @@ -30,7 +30,7 @@ func TestRegisterInterchainAccount(t *testing.T) { defer ctrl.Finish() icaKeeper := mock_types.NewMockICAControllerKeeper(ctrl) cmKeeper := mock_types.NewMockContractManagerKeeper(ctrl) - icak, ctx := testkeeper.InterchainTxsKeeper(t, cmKeeper, nil, icaKeeper, nil, nil) + icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, nil, icaKeeper, nil, nil) goCtx := sdk.WrapSDKContext(ctx) msgRegAcc := types.MsgRegisterInterchainAccount{ @@ -71,7 +71,7 @@ func TestSubmitTx(t *testing.T) { capabilityKeeper := mock_types.NewMockScopedKeeper(ctrl) refundKeeper := mock_types.NewMockFeeRefunderKeeper(ctrl) channelKeeper := mock_types.NewMockChannelKeeper(ctrl) - icak, ctx := testkeeper.InterchainTxsKeeper(t, cmKeeper, refundKeeper, icaKeeper, channelKeeper, capabilityKeeper) + icak, ctx, _ := testkeeper.InterchainTxsKeeper(t, cmKeeper, refundKeeper, icaKeeper, channelKeeper, capabilityKeeper) goCtx := sdk.WrapSDKContext(ctx) cosmosMsg := codectypes.Any{ diff --git a/x/interchaintxs/keeper/params_test.go b/x/interchaintxs/keeper/params_test.go index 03e79aa2b..8432fb6ec 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, nil) + k, ctx, _ := testkeeper.InterchainTxsKeeper(t, nil, nil, nil, nil, nil) params := types.DefaultParams() k.SetParams(ctx, params) diff --git a/x/transfer/ibc_handlers_test.go b/x/transfer/ibc_handlers_test.go index 274450540..2cca6f573 100644 --- a/x/transfer/ibc_handlers_test.go +++ b/x/transfer/ibc_handlers_test.go @@ -20,6 +20,16 @@ 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() @@ -29,9 +39,10 @@ func TestHandleAcknowledgement(t *testing.T) { 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) + txKeeper, infCtx, storeKey := 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{ @@ -84,62 +95,103 @@ func TestHandleAcknowledgement(t *testing.T) { p.Data = tokenBz // error during SudoResponse non contract - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(ctx), contractAddress, p, resACK.GetResult()).Return(nil, fmt.Errorf("SudoResponse error")) + 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") 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(2990), ctx.GasMeter().GasConsumed()) // error during SudoResponse contract - cmKeeper.EXPECT().SudoResponse(gomock.AssignableToTypeOf(ctx), contractAddress, p, resACK.GetResult()).Return(nil, fmt.Errorf("SudoResponse error")) + 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") 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, 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 - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Return(nil, fmt.Errorf("SudoError error")) + 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) // feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) require.NoError(t, err) + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) + require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) // error during SudoError contract - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Return(nil, fmt.Errorf("SudoError error")) + 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) require.NoError(t, err) + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) + require.Equal(t, uint64(2990), ctx.GasMeter().GasConsumed()) // success during SudoError non contract - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Return(nil, nil) + 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_non_contract"), ShouldBeWritten) + }).Return(nil, nil) cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) require.NoError(t, err) + require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudoerror_non_contract"))) // success during SudoError contract - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Return(nil, nil) + 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().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"))) // recoverable out of gas during SudoError non contract - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, error string) { - ctx.GasMeter().ConsumeGas(ctx.GasMeter().Limit()+1, "out of gas test") + 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(cachedCtx.GasMeter().Limit()+1, "out of gas test") }).Return(nil, fmt.Errorf("SudoError error")) cmKeeper.EXPECT().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") // FIXME: fix distribution during outofgas // 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)) // recoverable out of gas during SudoError contract - cmKeeper.EXPECT().SudoError(gomock.AssignableToTypeOf(ctx), contractAddress, p, errACK.GetError()).Do(func(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet, error string) { - ctx.GasMeter().ConsumeGas(ctx.GasMeter().Limit()+1, "out of gas test") + 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(cachedCtx.GasMeter().Limit()+1, "out of gas test") }).Return(nil, fmt.Errorf("SudoError error")) cmKeeper.EXPECT().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "ack") // FIXME: fix distribution during outofgas @@ -147,47 +199,52 @@ func TestHandleAcknowledgement(t *testing.T) { // feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) err = txModule.HandleAcknowledgement(ctx, p, errAckData, relayerAddress) require.NoError(t, err) + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) // 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)) 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 ctx.GasMeter().Limit() == cachedCtx.GasMeter().Limit()+transfer.GasReserve { gasReserved = true } - cachedCtx.GasMeter().ConsumeGas(1_000_000, "Sudo response consumption") + store := cachedCtx.KVStore(storeKey) + store.Set(ShouldBeWrittenKey("sudoresponse_non_contract_success"), ShouldBeWritten) }).Return(nil, nil) cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) // feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - consumedBefore := ctx.GasMeter().GasConsumed() err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) require.True(t, gasReserved) - require.Equal(t, consumedBefore+1_000_000, ctx.GasMeter().GasConsumed()) + require.Equal(t, uint64(3770), ctx.GasMeter().GasConsumed()) + require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudoresponse_non_contract_success"))) // contract - // refresh ctx 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 ctx.GasMeter().Limit() == cachedCtx.GasMeter().Limit()+transfer.GasReserve { gasReserved = true } - cachedCtx.GasMeter().ConsumeGas(1_000_000, "Sudo response consumption") + store := cachedCtx.KVStore(storeKey) + store.Set(ShouldBeWrittenKey("sudoresponse_contract_success"), ShouldBeWritten) }).Return(nil, nil) cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeAcknowledgementFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - consumedBefore = ctx.GasMeter().GasConsumed() err = txModule.HandleAcknowledgement(ctx, p, resAckData, relayerAddress) require.NoError(t, err) require.True(t, gasReserved) - require.Equal(t, consumedBefore+1_000_000, ctx.GasMeter().GasConsumed()) + require.Equal(t, uint64(3650), ctx.GasMeter().GasConsumed()) + require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudoresponse_contract_success"))) // not enough gas to reserve + not enough to make AddContractFailure failure after panic recover lowGasCtx := infCtx.WithGasMeter(sdk.NewGasMeter(keeper.GasReserve - 1)) 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(1, "Sudo response consumption") }).Return(nil, nil) // feeKeeper.EXPECT().DistributeAcknowledgementFee(lowGasCtx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) @@ -195,6 +252,7 @@ func TestHandleAcknowledgement(t *testing.T) { ctx.GasMeter().ConsumeGas(keeper.GasReserve, "out of gas") }) require.Panics(t, func() { txModule.HandleAcknowledgement(lowGasCtx, p, resAckData, relayerAddress) }) //nolint:errcheck // this is a test + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) } func TestHandleTimeout(t *testing.T) { @@ -206,9 +264,10 @@ func TestHandleTimeout(t *testing.T) { 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) + txKeeper, infCtx, storeKey := 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) @@ -234,6 +293,7 @@ func TestHandleTimeout(t *testing.T) { require.ErrorContains(t, err, "failed to decode address from bech32") // success non contract + ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) token = transfertypes.FungibleTokenPacketData{ Denom: "stake", Amount: "1000", @@ -248,14 +308,15 @@ func TestHandleTimeout(t *testing.T) { if ctx.GasMeter().Limit() == cachedCtx.GasMeter().Limit()+keeper.GasReserve { gasReserved = true } - cachedCtx.GasMeter().ConsumeGas(1_000_000, "Sudo timeout consumption") + store := cachedCtx.KVStore(storeKey) + store.Set(ShouldBeWrittenKey("sudotimeout_non_contract_success"), ShouldBeWritten) }).Return(nil, nil) cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) - consumedBefore := ctx.GasMeter().GasConsumed() err = txModule.HandleTimeout(ctx, p, relayerAddress) require.True(t, gasReserved) - require.Equal(t, consumedBefore+1_000_000, ctx.GasMeter().GasConsumed()) + require.Equal(t, uint64(3740), ctx.GasMeter().GasConsumed()) require.NoError(t, err) + require.Equal(t, ShouldBeWritten, store.Get(ShouldBeWrittenKey("sudotimeout_non_contract_success"))) // success contract ctx = infCtx.WithGasMeter(sdk.NewGasMeter(1_000_000_000_000)) @@ -264,43 +325,61 @@ func TestHandleTimeout(t *testing.T) { if ctx.GasMeter().Limit() == cachedCtx.GasMeter().Limit()+keeper.GasReserve { gasReserved = true } - cachedCtx.GasMeter().ConsumeGas(1_000_000, "Sudo timeout consumption") + store := cachedCtx.KVStore(storeKey) + store.Set(ShouldBeWrittenKey("sudotimeout_contract_success"), ShouldBeWritten) }).Return(nil, nil) cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(true) feeKeeper.EXPECT().DistributeTimeoutFee(ctx, relayerAddress, feetypes.NewPacketID(p.SourcePort, p.SourceChannel, p.Sequence)) - consumedBefore = ctx.GasMeter().GasConsumed() err = txModule.HandleTimeout(ctx, p, relayerAddress) require.True(t, gasReserved) - require.Equal(t, consumedBefore+1_000_000, ctx.GasMeter().GasConsumed()) 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 - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Return(nil, fmt.Errorf("SudoTimeout 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "timeout") cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) err = txModule.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) // error during SudoTimeOut contract - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Return(nil, fmt.Errorf("SudoTimeout 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().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "timeout") 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)) // out of gas during SudoTimeOut non contract - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - ctx.GasMeter().ConsumeGas(ctx.GasMeter().Limit()+1, "out of gas test") + 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(cachedCtx.GasMeter().Limit()+1, "out of gas test") }).Return(nil, fmt.Errorf("SudoTimeout error")) cmKeeper.EXPECT().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "timeout") // cmKeeper.EXPECT().HasContractInfo(ctx, sdk.MustAccAddressFromBech32(testutil.TestOwnerAddress)).Return(false) err = txModule.HandleTimeout(ctx, p, relayerAddress) require.NoError(t, err) + require.Empty(t, store.Get(ShouldNotBeWrittenKey)) // out of gas during SudoTimeOut contract - cmKeeper.EXPECT().SudoTimeout(gomock.AssignableToTypeOf(ctx), contractAddress, p).Do(func(ctx sdk.Context, senderAddress sdk.AccAddress, request channeltypes.Packet) { - ctx.GasMeter().ConsumeGas(ctx.GasMeter().Limit()+1, "out of gas test") + 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(cachedCtx.GasMeter().Limit()+1, "out of gas test") }).Return(nil, fmt.Errorf("SudoTimeout error")) cmKeeper.EXPECT().AddContractFailure(ctx, "channel-0", contractAddress.String(), p.GetSequence(), "timeout") // FIXME: make DistributeTimeoutFee during out of gas @@ -308,4 +387,5 @@ func TestHandleTimeout(t *testing.T) { // 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)) }