diff --git a/Makefile b/Makefile index 0eb1f95b2..2a264d822 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ LEDGER_ENABLED ?= true SDK_PACK := $(shell go list -m github.com/cosmos/cosmos-sdk | sed 's/ /\@/g') BINDIR ?= $(GOPATH)/bin SIMAPP = ./app -ENABLED_PROPOSALS := SudoContract,UpdateAdmin,ClearAdmin,PinCodes,UnpinCodes +ENABLED_PROPOSALS := MigrateContract,SudoContract,UpdateAdmin,ClearAdmin,PinCodes,UnpinCodes GO_VERSION=1.20.0 BUILDDIR ?= $(CURDIR)/build diff --git a/app/app.go b/app/app.go index 23c430921..ed1ac5743 100644 --- a/app/app.go +++ b/app/app.go @@ -135,7 +135,7 @@ import ( feetypes "github.com/neutron-org/neutron/x/feerefunder/types" - e2e "github.com/cosmos/interchain-security/testutil/integration" + e2e "github.com/cosmos/interchain-security/testutil/e2e" ccvconsumer "github.com/cosmos/interchain-security/x/ccv/consumer" ccvconsumerkeeper "github.com/cosmos/interchain-security/x/ccv/consumer/keeper" ccvconsumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" @@ -1153,21 +1153,21 @@ func (app *App) GetConsumerKeeper() ccvconsumerkeeper.Keeper { } // GetE2eBankKeeper implements the ConsumerApp interface. -func (app *App) GetTestBankKeeper() e2e.TestBankKeeper { +func (app *App) GetE2eBankKeeper() e2e.E2eBankKeeper { return app.BankKeeper } // GetE2eAccountKeeper implements the ConsumerApp interface. -func (app *App) GetTestAccountKeeper() e2e.TestAccountKeeper { +func (app *App) GetE2eAccountKeeper() e2e.E2eAccountKeeper { return app.AccountKeeper } // GetE2eSlashingKeeper implements the ConsumerApp interface. -func (app *App) GetTestSlashingKeeper() e2e.TestSlashingKeeper { +func (app *App) GetE2eSlashingKeeper() e2e.E2eSlashingKeeper { return app.SlashingKeeper } // GetE2eEvidenceKeeper implements the ConsumerApp interface. -func (app *App) GetTestEvidenceKeeper() e2e.TestEvidenceKeeper { +func (app *App) GetE2eEvidenceKeeper() e2e.E2eEvidenceKeeper { return app.EvidenceKeeper } diff --git a/cmd/neutrond/root.go b/cmd/neutrond/root.go index 68df020f0..229a25cc5 100644 --- a/cmd/neutrond/root.go +++ b/cmd/neutrond/root.go @@ -133,6 +133,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { func addModuleInitFlags(startCmd *cobra.Command) { crisis.AddModuleInitFlags(startCmd) + wasm.AddModuleInitFlags(startCmd) } func queryCommand() *cobra.Command { diff --git a/contracts/cwd_core.wasm b/contracts/cwd_core.wasm index 2a7d16368..e679f72a9 100644 Binary files a/contracts/cwd_core.wasm and b/contracts/cwd_core.wasm differ diff --git a/contracts/cwd_pre_propose_multiple.wasm b/contracts/cwd_pre_propose_multiple.wasm index 304771412..2337e0aa6 100644 Binary files a/contracts/cwd_pre_propose_multiple.wasm and b/contracts/cwd_pre_propose_multiple.wasm differ diff --git a/contracts/cwd_pre_propose_overrule.wasm b/contracts/cwd_pre_propose_overrule.wasm index 6259109c5..dd92f80c0 100644 Binary files a/contracts/cwd_pre_propose_overrule.wasm and b/contracts/cwd_pre_propose_overrule.wasm differ diff --git a/contracts/cwd_pre_propose_single.wasm b/contracts/cwd_pre_propose_single.wasm index 44e04e766..72f30670b 100644 Binary files a/contracts/cwd_pre_propose_single.wasm and b/contracts/cwd_pre_propose_single.wasm differ diff --git a/contracts/cwd_proposal_multiple.wasm b/contracts/cwd_proposal_multiple.wasm index fbdc83120..0889a6c04 100644 Binary files a/contracts/cwd_proposal_multiple.wasm and b/contracts/cwd_proposal_multiple.wasm differ diff --git a/contracts/cwd_proposal_single.wasm b/contracts/cwd_proposal_single.wasm index 1a001d0c1..22860dcc8 100644 Binary files a/contracts/cwd_proposal_single.wasm and b/contracts/cwd_proposal_single.wasm differ diff --git a/contracts/cwd_subdao_core.wasm b/contracts/cwd_subdao_core.wasm index fc95f9338..140eeb4c8 100644 Binary files a/contracts/cwd_subdao_core.wasm and b/contracts/cwd_subdao_core.wasm differ diff --git a/contracts/cwd_subdao_pre_propose_single.wasm b/contracts/cwd_subdao_pre_propose_single.wasm index 8f9ca9d0b..a14d4f7fa 100644 Binary files a/contracts/cwd_subdao_pre_propose_single.wasm and b/contracts/cwd_subdao_pre_propose_single.wasm differ diff --git a/contracts/cwd_subdao_proposal_single.wasm b/contracts/cwd_subdao_proposal_single.wasm index 9e98e2b29..c710289bf 100644 Binary files a/contracts/cwd_subdao_proposal_single.wasm and b/contracts/cwd_subdao_proposal_single.wasm differ diff --git a/contracts/cwd_subdao_timelock_single.wasm b/contracts/cwd_subdao_timelock_single.wasm index 48d28936e..fd2c01afb 100644 Binary files a/contracts/cwd_subdao_timelock_single.wasm and b/contracts/cwd_subdao_timelock_single.wasm differ diff --git a/contracts/lockdrop_vault.wasm b/contracts/lockdrop_vault.wasm index c31aa2fe1..670dec305 100644 Binary files a/contracts/lockdrop_vault.wasm and b/contracts/lockdrop_vault.wasm differ diff --git a/contracts/neutron_distribution.wasm b/contracts/neutron_distribution.wasm index aba9e2932..3864e6c35 100644 Binary files a/contracts/neutron_distribution.wasm and b/contracts/neutron_distribution.wasm differ diff --git a/contracts/neutron_reserve.wasm b/contracts/neutron_reserve.wasm index 169f6028e..74e0fd562 100644 Binary files a/contracts/neutron_reserve.wasm and b/contracts/neutron_reserve.wasm differ diff --git a/contracts/neutron_vault.wasm b/contracts/neutron_vault.wasm index 14e919582..c347aadef 100644 Binary files a/contracts/neutron_vault.wasm and b/contracts/neutron_vault.wasm differ diff --git a/contracts/neutron_voting_registry.wasm b/contracts/neutron_voting_registry.wasm index 54ed5b38e..bad786137 100644 Binary files a/contracts/neutron_voting_registry.wasm and b/contracts/neutron_voting_registry.wasm differ diff --git a/go.mod b/go.mod index ec14dff52..13103f11e 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.3 github.com/cosmos/cosmos-sdk v0.45.15 github.com/cosmos/gaia/v8 v8.0.1 - github.com/cosmos/ibc-go/v4 v4.3.0 - github.com/cosmos/interchain-security v1.0.1-0.20230419165046-6089b6121c33 + github.com/cosmos/ibc-go/v4 v4.3.1 + github.com/cosmos/interchain-security v1.2.0-multiden github.com/gogo/protobuf v1.3.3 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 @@ -138,6 +138,7 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tendermint/go-amino v0.16.0 // indirect + github.com/tendermint/spm v0.1.9 // indirect github.com/tidwall/btree v1.5.0 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.1 // indirect @@ -162,6 +163,6 @@ replace ( github.com/cosmos/admin-module => github.com/Ethernal-Tech/admin-module v0.0.0-20221102105340-e693f4d379c3 github.com/cosmos/cosmos-sdk => github.com/cosmos/cosmos-sdk v0.45.15-ics github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 - github.com/tendermint/tendermint => github.com/skip-mev/mev-cometbft v0.34.27-mev.17 + github.com/tendermint/tendermint => github.com/skip-mev/mev-cometbft v0.34.27-mev.18 google.golang.org/grpc => google.golang.org/grpc v1.33.2 ) diff --git a/go.sum b/go.sum index 2d224cebb..af9f1e13b 100644 --- a/go.sum +++ b/go.sum @@ -998,11 +998,11 @@ github.com/cosmos/iavl v0.19.5 h1:rGA3hOrgNxgRM5wYcSCxgQBap7fW82WZgY78V9po/iY= github.com/cosmos/iavl v0.19.5/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= github.com/cosmos/ibc-go v1.2.2/go.mod h1:XmYjsRFOs6Q9Cz+CSsX21icNoH27vQKb3squgnCOCbs= github.com/cosmos/ibc-go/v3 v3.0.0/go.mod h1:Mb+1NXiPOLd+CPFlOC6BKeAUaxXlhuWenMmRiUiSmwY= -github.com/cosmos/ibc-go/v4 v4.3.0 h1:yOzVsyZzsv4XPBux8gq+D0LhZn45yGWKjvT+6Vyo5no= -github.com/cosmos/ibc-go/v4 v4.3.0/go.mod h1:CcLvIoi9NNtIbNsxs4KjBGjYhlwqtsmXy1AKARKiMzQ= +github.com/cosmos/ibc-go/v4 v4.3.1 h1:xbg0CaCdxK3lvgGvSaI91ROOLd7s30UqEcexH6Ba4Ys= +github.com/cosmos/ibc-go/v4 v4.3.1/go.mod h1:89E+K9CxpkS/etLEcG026jPM/RSnVMcfesvRYp/0aKI= github.com/cosmos/interchain-accounts v0.2.6 h1:TV2M2g1/Rb9MCNw1YePdBKE0rcEczNj1RGHT+2iRYas= -github.com/cosmos/interchain-security v1.0.1-0.20230419165046-6089b6121c33 h1:gGeyeocVM771mWQUngG+INMnZLRn04xtTP8eHUKv8Fs= -github.com/cosmos/interchain-security v1.0.1-0.20230419165046-6089b6121c33/go.mod h1:ux46JqLoUfPq7FKXYXkAiqwzSiIfcLEgtv+plrv9aRA= +github.com/cosmos/interchain-security v1.2.0-multiden h1:NACl8426o4hXWYJBm3g2CFJwyXtcEHT5oUmelrdTjKg= +github.com/cosmos/interchain-security v1.2.0-multiden/go.mod h1:Y4onDsQuqkemGS7UqfyohahlBLt5gh1i8OVbPeMcuhA= github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-cosmos-go v0.12.2 h1:/XYaBlE2BJxtvpkHiBm97gFGSGmYGKunKyF3nNqAXZA= github.com/cosmos/ledger-cosmos-go v0.12.2/go.mod h1:ZcqYgnfNJ6lAXe4HPtWgarNEY+B74i+2/8MhZw4ziiI= @@ -2509,8 +2509,8 @@ github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= github.com/sivchari/tenv v1.7.0/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= -github.com/skip-mev/mev-cometbft v0.34.27-mev.17 h1:Sti5vpx6jvUzmLcsi22Xv9hf8eByXz6fKrKS3tACckw= -github.com/skip-mev/mev-cometbft v0.34.27-mev.17/go.mod h1:BcCbhKv7ieM0KEddnYXvQZR+pZykTKReJJYf7YC7qhw= +github.com/skip-mev/mev-cometbft v0.34.27-mev.18 h1:xoZRzzW4u4Rr4jjXUz7y05AoDZu8W5dGvgeg7jxrt0U= +github.com/skip-mev/mev-cometbft v0.34.27-mev.18/go.mod h1:BcCbhKv7ieM0KEddnYXvQZR+pZykTKReJJYf7YC7qhw= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= @@ -2643,6 +2643,7 @@ github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrn github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/spm v0.1.9 h1:O1DJF4evS8wgk5SZqRcO29irNNtKQmTpvQ0xFzUiczI= github.com/tendermint/spm v0.1.9/go.mod h1:iHgfQ5YOI6ONc9E7ugGQolVdfSMHpeXfZ/OpXuN/42Q= github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw= github.com/tendermint/tm-db v0.6.6/go.mod h1:wP8d49A85B7/erz/r4YbKssKw6ylsO/hKtFk7E1aWZI= diff --git a/neutron_dao.wasm b/neutron_dao.wasm deleted file mode 100644 index 05f676e9c..000000000 Binary files a/neutron_dao.wasm and /dev/null differ diff --git a/tests/e2e/interchain_security_test.go b/tests/e2e/interchain_security_test.go index 49cf36852..54f5d2a4e 100644 --- a/tests/e2e/interchain_security_test.go +++ b/tests/e2e/interchain_security_test.go @@ -7,7 +7,7 @@ import ( icssimapp "github.com/cosmos/interchain-security/testutil/ibc_testing" "github.com/stretchr/testify/suite" - e2e "github.com/cosmos/interchain-security/tests/integration" + "github.com/cosmos/interchain-security/tests/e2e" appConsumer "github.com/neutron-org/neutron/app" "github.com/neutron-org/neutron/testutil" 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/test_helpers.go b/testutil/test_helpers.go index 437c982b3..cc7343bfc 100644 --- a/testutil/test_helpers.go +++ b/testutil/test_helpers.go @@ -26,7 +26,8 @@ import ( clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" appProvider "github.com/cosmos/interchain-security/app/provider" - e2e "github.com/cosmos/interchain-security/testutil/integration" + e2e "github.com/cosmos/interchain-security/testutil/e2e" + ccvutils "github.com/cosmos/interchain-security/x/ccv/utils" tmtypes "github.com/tendermint/tendermint/types" "github.com/neutron-org/neutron/app" @@ -121,9 +122,9 @@ func (suite *IBCConnectionTestSuite) SetupTest() { suite.Require().True(len(providerValUpdates) == len(consumerBValUpdates), "initial valset not matching") for i := 0; i < len(providerValUpdates); i++ { - addr1, _ := ccv.TMCryptoPublicKeyToConsAddr(providerValUpdates[i].PubKey) - addr2, _ := ccv.TMCryptoPublicKeyToConsAddr(consumerAValUpdates[i].PubKey) - addr3, _ := ccv.TMCryptoPublicKeyToConsAddr(consumerBValUpdates[i].PubKey) + addr1, _ := ccvutils.TMCryptoPublicKeyToConsAddr(providerValUpdates[i].PubKey) + addr2, _ := ccvutils.TMCryptoPublicKeyToConsAddr(consumerAValUpdates[i].PubKey) + addr3, _ := ccvutils.TMCryptoPublicKeyToConsAddr(consumerBValUpdates[i].PubKey) suite.Require().True(bytes.Equal(addr1, addr2), "validator mismatch") suite.Require().True(bytes.Equal(addr1, addr3), "validator mismatch") } 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.go b/x/interchaintxs/keeper/ibc_handlers.go index 7644b0447..7b7506d33 100644 --- a/x/interchaintxs/keeper/ibc_handlers.go +++ b/x/interchaintxs/keeper/ibc_handlers.go @@ -34,28 +34,46 @@ func (k *Keeper) outOfGasRecovery( k.Logger(ctx).Debug("Out of gas", "Gas meter", gasMeter.String()) k.contractManagerKeeper.AddContractFailure(ctx, packet.SourceChannel, senderAddress.String(), packet.GetSequence(), failureAckType) - // FIXME: add distribution call } } -func (k *Keeper) createCachedContext(ctx sdk.Context) (cacheCtx sdk.Context, writeFn func(), newGasMeter sdk.GasMeter) { - gasLeft := ctx.GasMeter().Limit() - ctx.GasMeter().GasConsumed() +// createCachedContext creates a cached context for handling Sudo calls to CosmWasm smart-contracts. +// If there is an error during Sudo call, we can safely revert changes made in cached context. +func (k *Keeper) createCachedContext(ctx sdk.Context) (sdk.Context, func(), sdk.GasMeter) { + gasMeter := ctx.GasMeter() + // determines type of gas meter by its prefix: + // * BasicGasMeter - basic gas meter which is used for processing tx directly in block; + // * InfiniteGasMeter - is used to process txs during simulation calls. We don't need to create a limit for such meter, + // since it's infinite. + gasMeterIsLimited := strings.HasPrefix(ctx.GasMeter().String(), "BasicGasMeter") + + cacheCtx, writeFn := ctx.CacheContext() + + // if gas meter is limited: + // 1. calculate how much free gas left we have for a Sudo call; + // 2. If gasLeft less than reserved gas (GasReserved), we set gas limit for cached context to zero, meaning we can't + // process Sudo call; + // 3. If we have more gas left than reserved gas (GasReserved) for Sudo call, we set gas limit for cached context to + // difference between gas left and reserved gas: (gasLeft - GasReserve); + // + // GasReserve is the amount of gas on the context gas meter we need to reserve in order to add contract failure to keeper + // and process failed Sudo call + if gasMeterIsLimited { + gasLeft := gasMeter.Limit() - gasMeter.GasConsumed() + + var newLimit uint64 + if gasLeft < GasReserve { + newLimit = 0 + } else { + newLimit = gasLeft - GasReserve + } - var newLimit uint64 - if gasLeft < GasReserve { - newLimit = 0 - } else { - newLimit = gasLeft - GasReserve + gasMeter = sdk.NewGasMeter(newLimit) } - newGasMeter = sdk.NewGasMeter(newLimit) - - cacheCtx, writeFn = ctx.CacheContext() - if strings.HasPrefix(ctx.GasMeter().String(), "BasicGasMeter") { - cacheCtx = ctx.WithGasMeter(newGasMeter) - } + cacheCtx = cacheCtx.WithGasMeter(gasMeter) - return + return cacheCtx, writeFn, gasMeter } // HandleAcknowledgement passes the acknowledgement data to the appropriate contract via a Sudo call. @@ -93,6 +111,7 @@ func (k *Keeper) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.Pack k.contractManagerKeeper.AddContractFailure(ctx, packet.SourceChannel, icaOwner.GetContract().String(), packet.GetSequence(), "ack") k.Logger(ctx).Debug("HandleAcknowledgement: failed to Sudo contract on packet acknowledgement", "error", err) } else { + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) writeFn() } @@ -124,6 +143,7 @@ func (k *Keeper) HandleTimeout(ctx sdk.Context, packet channeltypes.Packet, rela k.contractManagerKeeper.AddContractFailure(ctx, packet.SourceChannel, icaOwner.GetContract().String(), packet.GetSequence(), "timeout") k.Logger(ctx).Error("HandleTimeout: failed to Sudo contract on packet timeout", "error", err) } else { + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) writeFn() } diff --git a/x/interchaintxs/keeper/ibc_handlers_test.go b/x/interchaintxs/keeper/ibc_handlers_test.go index c068977eb..4fd2e8f9b 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.go b/x/transfer/ibc_handlers.go index ddc4eb51a..a630201d9 100644 --- a/x/transfer/ibc_handlers.go +++ b/x/transfer/ibc_handlers.go @@ -37,24 +37,43 @@ func (im IBCModule) outOfGasRecovery( } } -func (im IBCModule) createCachedContext(ctx sdk.Context) (cacheCtx sdk.Context, writeFn func(), newGasMeter sdk.GasMeter) { - gasLeft := ctx.GasMeter().Limit() - ctx.GasMeter().GasConsumed() +// createCachedContext creates a cached context for handling Sudo calls to CosmWasm smart-contracts. +// If there is an error during Sudo call, we can safely revert changes made in cached context. +func (im *IBCModule) createCachedContext(ctx sdk.Context) (sdk.Context, func(), sdk.GasMeter) { + gasMeter := ctx.GasMeter() + // determines type of gas meter by its prefix: + // * BasicGasMeter - basic gas meter which is used for processing tx directly in block; + // * InfiniteGasMeter - is used to process txs during simulation calls. We don't need to create a limit for such meter, + // since it's infinite. + gasMeterIsLimited := strings.HasPrefix(ctx.GasMeter().String(), "BasicGasMeter") + + cacheCtx, writeFn := ctx.CacheContext() + + // if gas meter is limited: + // 1. calculate how much free gas left we have for a Sudo call; + // 2. If gasLeft less than reserved gas (GasReserved), we set gas limit for cached context to zero, meaning we can't + // process Sudo call; + // 3. If we have more gas left than reserved gas (GasReserved) for Sudo call, we set gas limit for cached context to + // difference between gas left and reserved gas: (gasLeft - GasReserve); + // + // GasReserve is the amount of gas on the context gas meter we need to reserve in order to add contract failure to keeper + // and process failed Sudo call + if gasMeterIsLimited { + gasLeft := gasMeter.Limit() - gasMeter.GasConsumed() + + var newLimit uint64 + if gasLeft < GasReserve { + newLimit = 0 + } else { + newLimit = gasLeft - GasReserve + } - var newLimit uint64 - if gasLeft < GasReserve { - newLimit = 0 - } else { - newLimit = gasLeft - GasReserve + gasMeter = sdk.NewGasMeter(newLimit) } - newGasMeter = sdk.NewGasMeter(newLimit) - - cacheCtx, writeFn = ctx.CacheContext() - if strings.HasPrefix(ctx.GasMeter().String(), "BasicGasMeter") { - cacheCtx = ctx.WithGasMeter(newGasMeter) - } + cacheCtx = cacheCtx.WithGasMeter(gasMeter) - return + return cacheCtx, writeFn, gasMeter } // HandleAcknowledgement passes the acknowledgement data to the appropriate contract via a Sudo call. @@ -89,6 +108,7 @@ func (im IBCModule) HandleAcknowledgement(ctx sdk.Context, packet channeltypes.P im.ContractManagerKeeper.AddContractFailure(ctx, packet.SourceChannel, senderAddress.String(), packet.GetSequence(), "ack") im.keeper.Logger(ctx).Debug("failed to Sudo contract on packet acknowledgement", err) } else { + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) writeFn() } @@ -125,6 +145,7 @@ func (im IBCModule) HandleTimeout(ctx sdk.Context, packet channeltypes.Packet, r im.ContractManagerKeeper.AddContractFailure(ctx, packet.SourceChannel, senderAddress.String(), packet.GetSequence(), "timeout") im.keeper.Logger(ctx).Debug("failed to Sudo contract on packet timeout", err) } else { + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) writeFn() } diff --git a/x/transfer/ibc_handlers_test.go b/x/transfer/ibc_handlers_test.go index be2c5ceb7..505cd5b93 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)) }