diff --git a/modules/apps/callbacks/callbacks_test.go b/modules/apps/callbacks/callbacks_test.go index 58e2dd971b8..ddf8cf40901 100644 --- a/modules/apps/callbacks/callbacks_test.go +++ b/modules/apps/callbacks/callbacks_test.go @@ -1,6 +1,7 @@ package ibccallbacks_test import ( + "encoding/json" "fmt" "testing" @@ -8,12 +9,17 @@ import ( sdkmath "cosmossdk.io/math" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + dbm "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + simapp "github.com/cosmos/ibc-go/v7/modules/apps/callbacks/testing/simapp" "github.com/cosmos/ibc-go/v7/modules/apps/callbacks/types" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ibctesting "github.com/cosmos/ibc-go/v7/testing" @@ -22,6 +28,28 @@ import ( const maxCallbackGas = uint64(1000000) +func init() { + ibctesting.DefaultTestingAppInit = SetupTestingApp +} + +// SetupTestingApp provides the duplicated simapp which is specific to the callbacks module on chain creation. +func SetupTestingApp() (ibctesting.TestingApp, map[string]json.RawMessage) { + db := dbm.NewMemDB() + encCdc := simapp.MakeTestEncodingConfig() + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, simtestutil.EmptyAppOptions{}) + return app, simapp.NewDefaultGenesisState(encCdc.Codec) +} + +// GetSimApp returns the duplicated SimApp from within the callbacks directory. +// This must be used instead of chain.GetSimApp() for tests within this directory. +func GetSimApp(chain *ibctesting.TestChain) *simapp.SimApp { + app, ok := chain.App.(*simapp.SimApp) + if !ok { + panic("chain is not a simapp.SimApp") + } + return app +} + // CallbacksTestSuite defines the needed instances and methods to test callbacks type CallbacksTestSuite struct { suite.Suite @@ -104,7 +132,7 @@ func (s *CallbacksTestSuite) SetupICATest() string { err = s.path.EndpointB.ChanOpenConfirm() s.Require().NoError(err) - interchainAccountAddr, found := s.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(s.chainB.GetContext(), s.path.EndpointA.ConnectionID, s.path.EndpointA.ChannelConfig.PortID) + interchainAccountAddr, found := GetSimApp(s.chainB).ICAHostKeeper.GetInterchainAccountAddress(s.chainB.GetContext(), s.path.EndpointA.ConnectionID, s.path.EndpointA.ChannelConfig.PortID) s.Require().True(found) // fund the interchain account on chainB @@ -145,8 +173,8 @@ func (s *CallbacksTestSuite) AssertHasExecutedExpectedCallback(callbackType type expStatefulEntries = 1 } - sourceStatefulCounter := s.chainA.GetSimApp().MockContractKeeper.GetStateEntryCounter(s.chainA.GetContext()) - destStatefulCounter := s.chainB.GetSimApp().MockContractKeeper.GetStateEntryCounter(s.chainB.GetContext()) + sourceStatefulCounter := GetSimApp(s.chainA).MockContractKeeper.GetStateEntryCounter(s.chainA.GetContext()) + destStatefulCounter := GetSimApp(s.chainB).MockContractKeeper.GetStateEntryCounter(s.chainB.GetContext()) switch callbackType { case "none": @@ -174,8 +202,8 @@ func (s *CallbacksTestSuite) AssertHasExecutedExpectedCallback(callbackType type } func (s *CallbacksTestSuite) AssertCallbackCounters(callbackType types.CallbackType) { - sourceCounters := s.chainA.GetSimApp().MockContractKeeper.Counters - destCounters := s.chainB.GetSimApp().MockContractKeeper.Counters + sourceCounters := GetSimApp(s.chainA).MockContractKeeper.Counters + destCounters := GetSimApp(s.chainB).MockContractKeeper.Counters switch callbackType { case "none": @@ -231,13 +259,13 @@ func (s *CallbacksTestSuite) AssertHasExecutedExpectedCallbackWithFee( // check forward relay balance s.Require().Equal( fee.RecvFee, - sdk.NewCoins(s.chainA.GetSimApp().BankKeeper.GetBalance(s.chainA.GetContext(), s.chainB.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), + sdk.NewCoins(GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainB.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), ) s.Require().Equal( fee.AckFee.Add(fee.TimeoutFee...), // ack fee paid, timeout fee refunded sdk.NewCoins( - s.chainA.GetSimApp().BankKeeper.GetBalance( + GetSimApp(s.chainA).BankKeeper.GetBalance( s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom), ).Sub(originalSenderBalance[0]), @@ -246,14 +274,14 @@ func (s *CallbacksTestSuite) AssertHasExecutedExpectedCallbackWithFee( // forward relay balance should be 0 s.Require().Equal( sdk.NewCoin(ibctesting.TestCoin.Denom, sdkmath.ZeroInt()), - s.chainA.GetSimApp().BankKeeper.GetBalance(s.chainA.GetContext(), s.chainB.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom), + GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainB.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom), ) // all fees should be returned as sender is the reverse relayer s.Require().Equal( fee.Total(), sdk.NewCoins( - s.chainA.GetSimApp().BankKeeper.GetBalance( + GetSimApp(s.chainA).BankKeeper.GetBalance( s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom), ).Sub(originalSenderBalance[0]), diff --git a/modules/apps/callbacks/fee_transfer_test.go b/modules/apps/callbacks/fee_transfer_test.go index 4e448d4a77d..f1b868d9d47 100644 --- a/modules/apps/callbacks/fee_transfer_test.go +++ b/modules/apps/callbacks/fee_transfer_test.go @@ -100,7 +100,7 @@ func (s *CallbacksTestSuite) TestIncentivizedTransferCallbacks() { fee := feetypes.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) s.ExecutePayPacketFeeMsg(fee) - preRelaySenderBalance := sdk.NewCoins(s.chainA.GetSimApp().BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) + preRelaySenderBalance := sdk.NewCoins(GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) s.ExecuteTransfer(tc.transferMemo) // we manually subtract the transfer amount from the preRelaySenderBalance because ExecuteTransfer // also relays the packet, which will trigger the fee payments. @@ -158,7 +158,7 @@ func (s *CallbacksTestSuite) TestIncentivizedTransferTimeoutCallbacks() { fee := feetypes.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) s.ExecutePayPacketFeeMsg(fee) - preRelaySenderBalance := sdk.NewCoins(s.chainA.GetSimApp().BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) + preRelaySenderBalance := sdk.NewCoins(GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) s.ExecuteTransferTimeout(tc.transferMemo, 1) // after incentivizing the packets @@ -174,13 +174,13 @@ func (s *CallbacksTestSuite) ExecutePayPacketFeeMsg(fee feetypes.Fee) { ) // fetch the account balance before fees are escrowed and assert the difference below - preEscrowBalance := s.chainA.GetSimApp().BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom) + preEscrowBalance := GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom) res, err := s.chainA.SendMsgs(msg) s.Require().NoError(err) s.Require().NotNil(res) - postEscrowBalance := s.chainA.GetSimApp().BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom) + postEscrowBalance := GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom) s.Require().Equal(postEscrowBalance.AddAmount(fee.Total().AmountOf(sdk.DefaultBondDenom)), preEscrowBalance) // register counterparty address on chainB diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index af30b87aac3..dfee31d0518 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -318,8 +318,8 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { s.Require().ErrorIs(tc.expError, err) } - sourceStatefulCounter := s.chainA.GetSimApp().MockContractKeeper.GetStateEntryCounter(s.chainA.GetContext()) - sourceCounters := s.chainA.GetSimApp().MockContractKeeper.Counters + sourceStatefulCounter := GetSimApp(s.chainA).MockContractKeeper.GetStateEntryCounter(s.chainA.GetContext()) + sourceCounters := GetSimApp(s.chainA).MockContractKeeper.Counters switch tc.expResult { case noExecution: @@ -471,8 +471,8 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { s.Require().ErrorIs(tc.expError, err) } - sourceStatefulCounter := s.chainA.GetSimApp().MockContractKeeper.GetStateEntryCounter(s.chainA.GetContext()) - sourceCounters := s.chainA.GetSimApp().MockContractKeeper.Counters + sourceStatefulCounter := GetSimApp(s.chainA).MockContractKeeper.GetStateEntryCounter(s.chainA.GetContext()) + sourceCounters := GetSimApp(s.chainA).MockContractKeeper.Counters // account for SendPacket succeeding switch tc.expResult { @@ -623,8 +623,8 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { s.Require().Equal(tc.expAck, ack) } - destStatefulCounter := s.chainB.GetSimApp().MockContractKeeper.GetStateEntryCounter(s.chainB.GetContext()) - destCounters := s.chainB.GetSimApp().MockContractKeeper.Counters + destStatefulCounter := GetSimApp(s.chainB).MockContractKeeper.GetStateEntryCounter(s.chainB.GetContext()) + destCounters := GetSimApp(s.chainB).MockContractKeeper.Counters switch tc.expResult { case noExecution: diff --git a/modules/apps/callbacks/ica_test.go b/modules/apps/callbacks/ica_test.go index 758336f0dd2..f831bcc715a 100644 --- a/modules/apps/callbacks/ica_test.go +++ b/modules/apps/callbacks/ica_test.go @@ -182,9 +182,9 @@ func (s *CallbacksTestSuite) buildICAMsgDelegatePacketData(icaAddress string, me // ensure chainB is allowed to execute stakingtypes.MsgDelegate params := icahosttypes.NewParams(true, []string{sdk.MsgTypeURL(msgDelegate)}) - s.chainB.GetSimApp().ICAHostKeeper.SetParams(s.chainB.GetContext(), params) + GetSimApp(s.chainB).ICAHostKeeper.SetParams(s.chainB.GetContext(), params) - data, err := icatypes.SerializeCosmosTx(s.chainA.GetSimApp().AppCodec(), []proto.Message{msgDelegate}, icatypes.EncodingProtobuf) + data, err := icatypes.SerializeCosmosTx(GetSimApp(s.chainA).AppCodec(), []proto.Message{msgDelegate}, icatypes.EncodingProtobuf) s.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ diff --git a/modules/apps/callbacks/testing/simapp/ante_handler.go b/modules/apps/callbacks/testing/simapp/ante_handler.go new file mode 100644 index 00000000000..1214324e66b --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/ante_handler.go @@ -0,0 +1,50 @@ +package simapp + +import ( + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + + ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" + ibcerrors "github.com/cosmos/ibc-go/v7/modules/core/errors" + "github.com/cosmos/ibc-go/v7/modules/core/keeper" +) + +// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC keeper. +type HandlerOptions struct { + ante.HandlerOptions + + IBCKeeper *keeper.Keeper +} + +// NewAnteHandler creates a new ante handler +func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { + if options.AccountKeeper == nil { + return nil, errorsmod.Wrap(ibcerrors.ErrLogic, "account keeper is required for AnteHandler") + } + if options.BankKeeper == nil { + return nil, errorsmod.Wrap(ibcerrors.ErrLogic, "bank keeper is required for AnteHandler") + } + if options.SignModeHandler == nil { + return nil, errorsmod.Wrap(ibcerrors.ErrLogic, "sign mode handler is required for AnteHandler") + } + + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker), + ante.NewValidateBasicDecorator(), + ante.NewTxTimeoutHeightDecorator(), + ante.NewValidateMemoDecorator(options.AccountKeeper), + ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), + ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker), + ante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators + ante.NewValidateSigCountDecorator(options.AccountKeeper), + ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), + ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), + ante.NewIncrementSequenceDecorator(options.AccountKeeper), + ibcante.NewRedundantRelayDecorator(options.IBCKeeper), + } + + return sdk.ChainAnteDecorators(anteDecorators...), nil +} diff --git a/modules/apps/callbacks/testing/simapp/app.go b/modules/apps/callbacks/testing/simapp/app.go new file mode 100644 index 00000000000..00e863edfcb --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/app.go @@ -0,0 +1,1014 @@ +package simapp + +import ( + "encoding/json" + "io" + "os" + "path/filepath" + + "github.com/spf13/cast" + + _ "github.com/cosmos/cosmos-sdk/client/docs/statik" // this is used for serving docs + + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/std" + "github.com/cosmos/cosmos-sdk/store/streaming" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + testdatapb "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/posthandler" + authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" + "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/consensus" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/evidence" + evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + "github.com/cosmos/cosmos-sdk/x/feegrant" + feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/group" + groupkeeper "github.com/cosmos/cosmos-sdk/x/group/keeper" + groupmodule "github.com/cosmos/cosmos-sdk/x/group/module" + "github.com/cosmos/cosmos-sdk/x/mint" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/cosmos/cosmos-sdk/x/params" + paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/upgrade" + upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/log" + + "github.com/cosmos/ibc-go/modules/capability" + capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + ica "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts" + icacontroller "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller" + icacontrollerkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icahost "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host" + icahostkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/keeper" + icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibcfee "github.com/cosmos/ibc-go/v7/modules/apps/29-fee" + ibcfeekeeper "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/keeper" + ibcfeetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + ibccallbacks "github.com/cosmos/ibc-go/v7/modules/apps/callbacks" + simappparams "github.com/cosmos/ibc-go/v7/modules/apps/callbacks/testing/simapp/params" + transfer "github.com/cosmos/ibc-go/v7/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v7/modules/core" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" + ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" + ibctestingtypes "github.com/cosmos/ibc-go/v7/testing/types" +) + +const appName = "SimApp" + +// IBC application testing ports +const ( + MockFeePort string = ibcmock.ModuleName + ibcfeetypes.ModuleName +) + +var ( + // DefaultNodeHome default home directories for the application daemon + DefaultNodeHome string + + // ModuleBasics defines the module BasicManager is in charge of setting up basic, + // non-dependant module elements, such as codec registration + // and genesis verification. + ModuleBasics = module.NewBasicManager( + auth.AppModuleBasic{}, + genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + bank.AppModuleBasic{}, + capability.AppModuleBasic{}, + staking.AppModuleBasic{}, + mint.AppModuleBasic{}, + distr.AppModuleBasic{}, + gov.NewAppModuleBasic( + []govclient.ProposalHandler{ + paramsclient.ProposalHandler, + upgradeclient.LegacyProposalHandler, + upgradeclient.LegacyCancelProposalHandler, + ibcclientclient.UpdateClientProposalHandler, + ibcclientclient.UpgradeProposalHandler, + }, + ), + groupmodule.AppModuleBasic{}, + params.AppModuleBasic{}, + crisis.AppModuleBasic{}, + slashing.AppModuleBasic{}, + ibc.AppModuleBasic{}, + ibctm.AppModuleBasic{}, + solomachine.AppModuleBasic{}, + feegrantmodule.AppModuleBasic{}, + upgrade.AppModuleBasic{}, + evidence.AppModuleBasic{}, + transfer.AppModuleBasic{}, + ibcmock.AppModuleBasic{}, + ica.AppModuleBasic{}, + authzmodule.AppModuleBasic{}, + vesting.AppModuleBasic{}, + ibcfee.AppModuleBasic{}, + consensus.AppModuleBasic{}, + ) + + // module account permissions + maccPerms = map[string][]string{ + authtypes.FeeCollectorName: nil, + distrtypes.ModuleName: nil, + minttypes.ModuleName: {authtypes.Minter}, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + govtypes.ModuleName: {authtypes.Burner}, + ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + ibcfeetypes.ModuleName: nil, + icatypes.ModuleName: nil, + ibcmock.ModuleName: nil, + } +) + +var ( + _ runtime.AppI = (*SimApp)(nil) + _ servertypes.Application = (*SimApp)(nil) +) + +// SimApp extends an ABCI application, but with most of its parameters exported. +// They are exported for convenience in creating helper functions, as object +// capabilities aren't needed for testing. +type SimApp struct { + *baseapp.BaseApp + legacyAmino *codec.LegacyAmino + appCodec codec.Codec + txConfig client.TxConfig + interfaceRegistry types.InterfaceRegistry + + // keys to access the substores + keys map[string]*storetypes.KVStoreKey + tkeys map[string]*storetypes.TransientStoreKey + memKeys map[string]*storetypes.MemoryStoreKey + + // keepers + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + CapabilityKeeper *capabilitykeeper.Keeper + StakingKeeper *stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + MintKeeper mintkeeper.Keeper + DistrKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + CrisisKeeper *crisiskeeper.Keeper + UpgradeKeeper *upgradekeeper.Keeper + ParamsKeeper paramskeeper.Keeper + AuthzKeeper authzkeeper.Keeper + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + IBCFeeKeeper ibcfeekeeper.Keeper + ICAControllerKeeper icacontrollerkeeper.Keeper + ICAHostKeeper icahostkeeper.Keeper + EvidenceKeeper evidencekeeper.Keeper + TransferKeeper ibctransferkeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + GroupKeeper groupkeeper.Keeper + ConsensusParamsKeeper consensusparamkeeper.Keeper + + // make scoped keepers public for test purposes + ScopedIBCKeeper capabilitykeeper.ScopedKeeper + ScopedTransferKeeper capabilitykeeper.ScopedKeeper + ScopedFeeMockKeeper capabilitykeeper.ScopedKeeper + ScopedICAControllerKeeper capabilitykeeper.ScopedKeeper + ScopedICAHostKeeper capabilitykeeper.ScopedKeeper + ScopedIBCMockKeeper capabilitykeeper.ScopedKeeper + ScopedICAMockKeeper capabilitykeeper.ScopedKeeper + + // mock contract keeper used for testing + MockContractKeeper ContractKeeper + + // make IBC modules public for test purposes + // these modules are never directly routed to by the IBC Router + ICAAuthModule ibcmock.IBCModule + FeeMockModule ibcmock.IBCModule + + // the module manager + ModuleManager *module.Manager + + // simulation manager + simulationManager *module.SimulationManager + + // module configurator + configurator module.Configurator +} + +func init() { + userHomeDir, err := os.UserHomeDir() + if err != nil { + panic(err) + } + + DefaultNodeHome = filepath.Join(userHomeDir, ".simapp") +} + +// NewSimApp returns a reference to an initialized SimApp. +func NewSimApp( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + appOpts servertypes.AppOptions, + baseAppOptions ...func(*baseapp.BaseApp), +) *SimApp { + encodingConfig := makeEncodingConfig() + + appCodec := encodingConfig.Codec + legacyAmino := encodingConfig.Amino + interfaceRegistry := encodingConfig.InterfaceRegistry + txConfig := encodingConfig.TxConfig + + // Below we could construct and set an application specific mempool and + // ABCI 1.0 PrepareProposal and ProcessProposal handlers. These defaults are + // already set in the SDK's BaseApp, this shows an example of how to override + // them. + // + // Example: + // + // bApp := baseapp.NewBaseApp(...) + // nonceMempool := mempool.NewSenderNonceMempool() + // abciPropHandler := NewDefaultProposalHandler(nonceMempool, bApp) + // + // bApp.SetMempool(nonceMempool) + // bApp.SetPrepareProposal(abciPropHandler.PrepareProposalHandler()) + // bApp.SetProcessProposal(abciPropHandler.ProcessProposalHandler()) + // + // Alternatively, you can construct BaseApp options, append those to + // baseAppOptions and pass them to NewBaseApp. + // + // Example: + // + // prepareOpt = func(app *baseapp.BaseApp) { + // abciPropHandler := baseapp.NewDefaultProposalHandler(nonceMempool, app) + // app.SetPrepareProposal(abciPropHandler.PrepareProposalHandler()) + // } + // baseAppOptions = append(baseAppOptions, prepareOpt) + + bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...) + bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetVersion(version.Version) + bApp.SetInterfaceRegistry(interfaceRegistry) + bApp.SetTxEncoder(txConfig.TxEncoder()) + + keys := sdk.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, crisistypes.StoreKey, + minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, + govtypes.StoreKey, group.StoreKey, paramstypes.StoreKey, + upgradetypes.StoreKey, feegrant.StoreKey, + evidencetypes.StoreKey, consensusparamtypes.StoreKey, authzkeeper.StoreKey, + ibctransfertypes.StoreKey, icacontrollertypes.StoreKey, icahosttypes.StoreKey, capabilitytypes.StoreKey, + ibcfeetypes.StoreKey, ibcexported.StoreKey, + ) + + tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + // NOTE: The ibcmock.MemStoreKey is just mounted for testing purposes. Actual applications should + // not include this key. + memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, ibcmock.MemStoreKey) + + // load state streaming if enabled + if _, _, err := streaming.LoadStreamingServices(bApp, appOpts, appCodec, logger, keys); err != nil { + logger.Error("failed to load state streaming", "err", err) + os.Exit(1) + } + + app := &SimApp{ + BaseApp: bApp, + legacyAmino: legacyAmino, + appCodec: appCodec, + txConfig: txConfig, + interfaceRegistry: interfaceRegistry, + keys: keys, + tkeys: tkeys, + memKeys: memKeys, + } + + app.ParamsKeeper = initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) + + // set the BaseApp's parameter store + app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper(appCodec, keys[consensusparamtypes.StoreKey], authtypes.NewModuleAddress(govtypes.ModuleName).String()) + bApp.SetParamStore(&app.ConsensusParamsKeeper) + + // add capability keeper and ScopeToModule for ibc module + app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) + + scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibcexported.ModuleName) + scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) + scopedICAControllerKeeper := app.CapabilityKeeper.ScopeToModule(icacontrollertypes.SubModuleName) + scopedICAHostKeeper := app.CapabilityKeeper.ScopeToModule(icahosttypes.SubModuleName) + + // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do + // not replicate if you do not need to test core IBC or light clients. + scopedIBCMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName) + scopedFeeMockKeeper := app.CapabilityKeeper.ScopeToModule(MockFeePort) + scopedICAMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName + icacontrollertypes.SubModuleName) + + // seal capability keeper after scoping modules + // Applications that wish to enforce statically created ScopedKeepers should call `Seal` after creating + // their scoped modules in `NewApp` with `ScopeToModule` + app.CapabilityKeeper.Seal() + + // SDK module keepers + app.AccountKeeper = authkeeper.NewAccountKeeper(appCodec, keys[authtypes.StoreKey], authtypes.ProtoBaseAccount, maccPerms, sdk.GetConfig().GetBech32AccountAddrPrefix(), authtypes.NewModuleAddress(govtypes.ModuleName).String()) + + app.BankKeeper = bankkeeper.NewBaseKeeper( + appCodec, + keys[banktypes.StoreKey], + app.AccountKeeper, + BlockedAddresses(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper = stakingkeeper.NewKeeper( + appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + app.MintKeeper = mintkeeper.NewKeeper(appCodec, keys[minttypes.StoreKey], app.StakingKeeper, app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + + app.DistrKeeper = distrkeeper.NewKeeper(appCodec, keys[distrtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + + app.SlashingKeeper = slashingkeeper.NewKeeper( + appCodec, legacyAmino, keys[slashingtypes.StoreKey], app.StakingKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + invCheckPeriod := cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)) + app.CrisisKeeper = crisiskeeper.NewKeeper(appCodec, keys[crisistypes.StoreKey], invCheckPeriod, + app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + + app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), + ) + + app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper) + + groupConfig := group.DefaultConfig() + /* + Example of setting group params: + groupConfig.MaxMetadataLen = 1000 + */ + app.GroupKeeper = groupkeeper.NewKeeper(keys[group.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper, groupConfig) + + skipUpgradeHeights := map[int64]bool{} + for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { + skipUpgradeHeights[int64(h)] = true + } + homePath := cast.ToString(appOpts.Get(flags.FlagHome)) + // set the governance module account as the authority for conducting upgrades + app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + + // IBC Keepers + + app.IBCKeeper = ibckeeper.NewKeeper( + appCodec, keys[ibcexported.StoreKey], app.GetSubspace(ibcexported.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // NOTE: The mock ContractKeeper is only created for testing. + // Real applications should not use the mock ContractKeeper + app.MockContractKeeper = NewContractKeeper(memKeys[ibcmock.MemStoreKey]) + + // register the proposal types + govRouter := govv1beta1.NewRouter() + govRouter.AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler). + AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). + AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)). + AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) + + govConfig := govtypes.DefaultConfig() + /* + Example of setting gov params: + govConfig.MaxMetadataLen = 10000 + */ + govKeeper := govkeeper.NewKeeper( + appCodec, keys[govtypes.StoreKey], app.AccountKeeper, app.BankKeeper, + app.StakingKeeper, app.MsgServiceRouter(), govConfig, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // Set legacy router for backwards compatibility with gov v1beta1 + govKeeper.SetLegacyRouter(govRouter) + + app.GovKeeper = *govKeeper.SetHooks( + govtypes.NewMultiGovHooks( + // register the governance hooks + ), + ) + + // IBC Fee Module keeper + app.IBCFeeKeeper = ibcfeekeeper.NewKeeper( + appCodec, keys[ibcfeetypes.StoreKey], + app.IBCKeeper.ChannelKeeper, // may be replaced with IBC middleware + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, + ) + + // ICA Controller keeper + app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper( + appCodec, keys[icacontrollertypes.StoreKey], app.GetSubspace(icacontrollertypes.SubModuleName), + app.IBCFeeKeeper, // use ics29 fee as ics4Wrapper in middleware stack + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + scopedICAControllerKeeper, app.MsgServiceRouter(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // ICA Host keeper + app.ICAHostKeeper = icahostkeeper.NewKeeper( + appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), + app.IBCFeeKeeper, // use ics29 fee as ics4Wrapper in middleware stack + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.AccountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // Create IBC Router + ibcRouter := porttypes.NewRouter() + + // Middleware Stacks + maxCallbackGas := uint64(1_000_000) + + // Create Transfer Keeper and pass IBCFeeKeeper as expected Channel and PortKeeper + // since fee middleware will wrap the IBCKeeper for underlying application. + // NOTE: the Transfer Keeper's ICS4Wrapper can later be replaced. + app.TransferKeeper = ibctransferkeeper.NewKeeper( + appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName), + app.IBCFeeKeeper, // ISC4 Wrapper: fee IBC middleware + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // Mock Module Stack + + // Mock Module setup for testing IBC and also acts as the interchain accounts authentication module + // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do + // not replicate if you do not need to test core IBC or light clients. + mockModule := ibcmock.NewAppModule(&app.IBCKeeper.PortKeeper) + + // The mock module is used for testing IBC + mockIBCModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp(ibcmock.ModuleName, scopedIBCMockKeeper)) + ibcRouter.AddRoute(ibcmock.ModuleName, mockIBCModule) + + // Create Transfer Stack + // SendPacket, since it is originating from the application to core IBC: + // transferKeeper.SendPacket -> fee.SendPacket -> callbacks.SendPacket -> channel.SendPacket + + // RecvPacket, message that originates from core IBC and goes down to app, the flow is the other way + // channel.RecvPacket -> callbacks.OnRecvPacket -> fee.OnRecvPacket -> transfer.OnRecvPacket + + // transfer stack contains (from top to bottom): + // - IBC Callbacks Middleware + // - IBC Fee Middleware + // - Transfer + + // create IBC module from bottom to top of stack + var transferStack porttypes.IBCModule + transferStack = transfer.NewIBCModule(app.TransferKeeper) + transferStack = ibcfee.NewIBCMiddleware(transferStack, app.IBCFeeKeeper) + transferStack = ibccallbacks.NewIBCMiddleware(transferStack, app.IBCFeeKeeper, app.MockContractKeeper, maxCallbackGas) + // Since the callbacks middleware itself is an ics4wrapper, it needs to be passed to the transfer keeper + app.TransferKeeper.WithICS4Wrapper(transferStack.(porttypes.Middleware)) + + // Add transfer stack to IBC Router + ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack) + + // Create Interchain Accounts Stack + // SendPacket, since it is originating from the application to core IBC: + // icaAuthModuleKeeper.SendTx -> icaController.SendPacket -> fee.SendPacket -> callbacks.SendPacket -> channel.SendPacket + + // initialize ICA module with mock module as the authentication module on the controller side + var icaControllerStack porttypes.IBCModule + icaControllerStack = ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp("", scopedICAMockKeeper)) + app.ICAAuthModule = icaControllerStack.(ibcmock.IBCModule) + icaControllerStack = icacontroller.NewIBCMiddleware(icaControllerStack, app.ICAControllerKeeper) + icaControllerStack = ibcfee.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper) + icaControllerStack = ibccallbacks.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper, app.MockContractKeeper, maxCallbackGas) + // Since the callbacks middleware itself is an ics4wrapper, it needs to be passed to the ica controller keeper + app.ICAControllerKeeper.WithICS4Wrapper(icaControllerStack.(porttypes.Middleware)) + + // RecvPacket, message that originates from core IBC and goes down to app, the flow is: + // channel.RecvPacket -> callbacks.OnRecvPacket -> fee.OnRecvPacket -> icaHost.OnRecvPacket + + var icaHostStack porttypes.IBCModule + icaHostStack = icahost.NewIBCModule(app.ICAHostKeeper) + icaHostStack = ibcfee.NewIBCMiddleware(icaHostStack, app.IBCFeeKeeper) + + // Add host, controller & ica auth modules to IBC router + ibcRouter. + // the ICA Controller middleware needs to be explicitly added to the IBC Router because the + // ICA controller module owns the port capability for ICA. The ICA authentication module + // owns the channel capability. + AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). + AddRoute(icahosttypes.SubModuleName, icaHostStack). + AddRoute(ibcmock.ModuleName+icacontrollertypes.SubModuleName, icaControllerStack) // ica with mock auth module stack route to ica (top level of middleware stack) + + // Create Mock IBC Fee module stack for testing + // SendPacket, since it is originating from the application to core IBC: + // mockModule.SendPacket -> fee.SendPacket -> channel.SendPacket + + // OnRecvPacket, message that originates from core IBC and goes down to app, the flow is the otherway + // channel.RecvPacket -> fee.OnRecvPacket -> mockModule.OnRecvPacket + + // OnAcknowledgementPacket as this is where fee's are paid out + // mockModule.OnAcknowledgementPacket -> fee.OnAcknowledgementPacket -> channel.OnAcknowledgementPacket + + // create fee wrapped mock module + feeMockModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp(MockFeePort, scopedFeeMockKeeper)) + app.FeeMockModule = feeMockModule + var feeWithMockModule porttypes.Middleware = ibcfee.NewIBCMiddleware(feeMockModule, app.IBCFeeKeeper) + feeWithMockModule = ibccallbacks.NewIBCMiddleware(feeWithMockModule, app.IBCFeeKeeper, app.MockContractKeeper, maxCallbackGas) + ibcRouter.AddRoute(MockFeePort, feeWithMockModule) + + // Seal the IBC Router + app.IBCKeeper.SetRouter(ibcRouter) + + // create evidence keeper with router + evidenceKeeper := evidencekeeper.NewKeeper( + appCodec, keys[evidencetypes.StoreKey], app.StakingKeeper, app.SlashingKeeper, + ) + // If evidence needs to be handled for the app, set routes in router here and seal + app.EvidenceKeeper = *evidenceKeeper + + // **** Module Options **** + + // NOTE: we may consider parsing `appOpts` inside module constructors. For the moment + // we prefer to be more strict in what arguments the modules expect. + skipGenesisInvariants := cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants)) + + // NOTE: Any module instantiated in the module manager that is later modified + // must be passed by reference here. + app.ModuleManager = module.NewManager( + // SDK app modules + genutil.NewAppModule( + app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, + encodingConfig.TxConfig, + ), + auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), + capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), + crisis.NewAppModule(app.CrisisKeeper, skipGenesisInvariants, app.GetSubspace(crisistypes.ModuleName)), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), + mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + upgrade.NewAppModule(app.UpgradeKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + params.NewAppModule(app.ParamsKeeper), + authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + groupmodule.NewAppModule(appCodec, app.GroupKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), + + // IBC modules + ibc.NewAppModule(app.IBCKeeper), + transfer.NewAppModule(app.TransferKeeper), + ibcfee.NewAppModule(app.IBCFeeKeeper), + ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper), + mockModule, + ) + + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + // NOTE: staking module is required if HistoricalEntries param > 0 + // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) + app.ModuleManager.SetOrderBeginBlockers( + upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, + evidencetypes.ModuleName, stakingtypes.ModuleName, ibcexported.ModuleName, ibctransfertypes.ModuleName, authtypes.ModuleName, + banktypes.ModuleName, govtypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, authz.ModuleName, feegrant.ModuleName, + paramstypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, group.ModuleName, consensusparamtypes.ModuleName, + ) + + app.ModuleManager.SetOrderEndBlockers( + crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, ibcexported.ModuleName, ibctransfertypes.ModuleName, + capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, + minttypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, + upgradetypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, group.ModuleName, consensusparamtypes.ModuleName, + ) + + // NOTE: The genutils module must occur after staking so that pools are + // properly initialized with tokens from genesis accounts. + // NOTE: The genutils module must also occur after auth so that it can access the params from auth. + // NOTE: Capability module must occur first so that it can initialize any capabilities + // so that other modules that want to create or claim capabilities afterwards in InitChain + // can do so safely. + + genesisModuleOrder := []string{ + capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName, + slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, + ibcexported.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, ibctransfertypes.ModuleName, + icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, + vestingtypes.ModuleName, group.ModuleName, consensusparamtypes.ModuleName, + } + app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...) + app.ModuleManager.SetOrderExportGenesis(genesisModuleOrder...) + + // Uncomment if you want to set a custom migration order here. + // app.ModuleManager.SetOrderMigrations(custom order) + + app.ModuleManager.RegisterInvariants(app.CrisisKeeper) + app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) + app.ModuleManager.RegisterServices(app.configurator) + + autocliv1.RegisterQueryServer(app.GRPCQueryRouter(), runtimeservices.NewAutoCLIQueryService(app.ModuleManager.Modules)) + + reflectionSvc, err := runtimeservices.NewReflectionService() + if err != nil { + panic(err) + } + reflectionv1.RegisterReflectionServiceServer(app.GRPCQueryRouter(), reflectionSvc) + + // add test gRPC service for testing gRPC queries in isolation + testdatapb.RegisterQueryServer(app.GRPCQueryRouter(), testdatapb.QueryImpl{}) + + // create the simulation manager and define the order of the modules for deterministic simulations + // + // NOTE: this is not required apps that don't use the simulator for fuzz testing + // transactions + overrideModules := map[string]module.AppModuleSimulation{ + authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + } + app.simulationManager = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules) + + app.simulationManager.RegisterStoreDecoders() + + // initialize stores + app.MountKVStores(keys) + app.MountTransientStores(tkeys) + app.MountMemoryStores(memKeys) + + // initialize BaseApp + app.SetInitChainer(app.InitChainer) + app.SetBeginBlocker(app.BeginBlocker) + app.SetEndBlocker(app.EndBlocker) + app.setAnteHandler(encodingConfig.TxConfig) + + // In v0.46, the SDK introduces _postHandlers_. PostHandlers are like + // antehandlers, but are run _after_ the `runMsgs` execution. They are also + // defined as a chain, and have the same signature as antehandlers. + // + // In baseapp, postHandlers are run in the same store branch as `runMsgs`, + // meaning that both `runMsgs` and `postHandler` state will be committed if + // both are successful, and both will be reverted if any of the two fails. + // + // The SDK exposes a default postHandlers chain, which comprises of only + // one decorator: the Transaction Tips decorator. However, some chains do + // not need it by default, so feel free to comment the next line if you do + // not need tips. + // To read more about tips: + // https://docs.cosmos.network/main/core/tips.html + // + // Please note that changing any of the anteHandler or postHandler chain is + // likely to be a state-machine breaking change, which needs a coordinated + // upgrade. + app.setPostHandler() + + app.setupUpgradeHandlers() + app.setupUpgradeStoreLoaders() + + if loadLatest { + if err := app.LoadLatestVersion(); err != nil { + logger.Error("error on loading last version", "err", err) + os.Exit(1) + } + } + + app.ScopedIBCKeeper = scopedIBCKeeper + app.ScopedTransferKeeper = scopedTransferKeeper + app.ScopedICAControllerKeeper = scopedICAControllerKeeper + app.ScopedICAHostKeeper = scopedICAHostKeeper + + // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do + // note replicate if you do not need to test core IBC or light clients. + app.ScopedIBCMockKeeper = scopedIBCMockKeeper + app.ScopedICAMockKeeper = scopedICAMockKeeper + app.ScopedFeeMockKeeper = scopedFeeMockKeeper + + return app +} + +func (app *SimApp) setAnteHandler(txConfig client.TxConfig) { + anteHandler, err := NewAnteHandler( + HandlerOptions{ + HandlerOptions: ante.HandlerOptions{ + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + SignModeHandler: txConfig.SignModeHandler(), + FeegrantKeeper: app.FeeGrantKeeper, + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + }, + IBCKeeper: app.IBCKeeper, + }, + ) + if err != nil { + panic(err) + } + + app.SetAnteHandler(anteHandler) +} + +func (app *SimApp) setPostHandler() { + postHandler, err := posthandler.NewPostHandler( + posthandler.HandlerOptions{}, + ) + if err != nil { + panic(err) + } + + app.SetPostHandler(postHandler) +} + +// Name returns the name of the App +func (app *SimApp) Name() string { return app.BaseApp.Name() } + +// BeginBlocker application updates every begin block +func (app *SimApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { + return app.ModuleManager.BeginBlock(ctx, req) +} + +// EndBlocker application updates every end block +func (app *SimApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + return app.ModuleManager.EndBlock(ctx, req) +} + +// Configurator returns the configurator for the app +func (app *SimApp) Configurator() module.Configurator { + return app.configurator +} + +// InitChainer application update at chain initialization +func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState GenesisState + if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } + app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()) + return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) +} + +// LoadHeight loads a particular height +func (app *SimApp) LoadHeight(height int64) error { + return app.LoadVersion(height) +} + +// LegacyAmino returns SimApp's amino codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *SimApp) LegacyAmino() *codec.LegacyAmino { + return app.legacyAmino +} + +// AppCodec returns SimApp's app codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *SimApp) AppCodec() codec.Codec { + return app.appCodec +} + +// InterfaceRegistry returns SimApp's InterfaceRegistry +func (app *SimApp) InterfaceRegistry() types.InterfaceRegistry { + return app.interfaceRegistry +} + +// TxConfig returns SimApp's TxConfig +func (app *SimApp) TxConfig() client.TxConfig { + return app.txConfig +} + +// DefaultGenesis returns a default genesis from the registered AppModuleBasic's. +func (app *SimApp) DefaultGenesis() map[string]json.RawMessage { + return ModuleBasics.DefaultGenesis(app.appCodec) +} + +// GetKey returns the KVStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *SimApp) GetKey(storeKey string) *storetypes.KVStoreKey { + return app.keys[storeKey] +} + +// GetTKey returns the TransientStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *SimApp) GetTKey(storeKey string) *storetypes.TransientStoreKey { + return app.tkeys[storeKey] +} + +// GetMemKey returns the MemStoreKey for the provided mem key. +// +// NOTE: This is solely used for testing purposes. +func (app *SimApp) GetMemKey(storeKey string) *storetypes.MemoryStoreKey { + return app.memKeys[storeKey] +} + +// GetSubspace returns a param subspace for a given module name. +// +// NOTE: This is solely to be used for testing purposes. +func (app *SimApp) GetSubspace(moduleName string) paramstypes.Subspace { + subspace, _ := app.ParamsKeeper.GetSubspace(moduleName) + return subspace +} + +// SimulationManager implements the SimulationApp interface +func (app *SimApp) SimulationManager() *module.SimulationManager { + return app.simulationManager +} + +// RegisterAPIRoutes registers all application module routes with the provided +// API server. +func (*SimApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { + clientCtx := apiSvr.ClientCtx + // Register new tx routes from grpc-gateway. + authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register new tendermint queries routes from grpc-gateway. + tmservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register node gRPC service for grpc-gateway. + nodeservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register grpc-gateway routes for all modules. + ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // register swagger API from root so that other applications can override easily + if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil { + panic(err) + } +} + +// RegisterTxService implements the Application.RegisterTxService method. +func (app *SimApp) RegisterTxService(clientCtx client.Context) { + authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry) +} + +// RegisterTendermintService implements the Application.RegisterTendermintService method. +func (app *SimApp) RegisterTendermintService(clientCtx client.Context) { + tmservice.RegisterTendermintService( + clientCtx, + app.BaseApp.GRPCQueryRouter(), + app.interfaceRegistry, + app.Query, + ) +} + +func (app *SimApp) RegisterNodeService(clientCtx client.Context) { + nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter()) +} + +// GetMaccPerms returns a copy of the module account permissions +// +// NOTE: This is solely to be used for testing purposes. +func GetMaccPerms() map[string][]string { + dupMaccPerms := make(map[string][]string) + for k, v := range maccPerms { + dupMaccPerms[k] = v + } + + return dupMaccPerms +} + +// BlockedAddresses returns all the app's blocked account addresses. +func BlockedAddresses() map[string]bool { + modAccAddrs := make(map[string]bool) + for acc := range GetMaccPerms() { + modAccAddrs[authtypes.NewModuleAddress(acc).String()] = true + } + + // allow the following addresses to receive funds + delete(modAccAddrs, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + delete(modAccAddrs, authtypes.NewModuleAddress(ibcmock.ModuleName).String()) + + return modAccAddrs +} + +// initParamsKeeper init params keeper and its subspaces +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey) paramskeeper.Keeper { + paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey) + + // TODO: ibc module subspaces can be removed after migration of params + // https://github.com/cosmos/ibc-go/issues/2010 + paramsKeeper.Subspace(ibctransfertypes.ModuleName) + paramsKeeper.Subspace(ibcexported.ModuleName) + paramsKeeper.Subspace(icacontrollertypes.SubModuleName) + paramsKeeper.Subspace(icahosttypes.SubModuleName) + + return paramsKeeper +} + +func makeEncodingConfig() simappparams.EncodingConfig { + encodingConfig := simappparams.MakeTestEncodingConfig() + std.RegisterLegacyAminoCodec(encodingConfig.Amino) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) + ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) + return encodingConfig +} + +// IBC Upgrade examples +// setupUpgradeHandlers sets all necessary upgrade handlers for testing purposes +func (*SimApp) setupUpgradeHandlers() {} + +// setupUpgradeStoreLoaders sets all necessary store loaders required by upgrades. +func (*SimApp) setupUpgradeStoreLoaders() {} + +// IBC TestingApp functions + +// GetBaseApp implements the TestingApp interface. +func (app *SimApp) GetBaseApp() *baseapp.BaseApp { + return app.BaseApp +} + +// GetStakingKeeper implements the TestingApp interface. +func (app *SimApp) GetStakingKeeper() ibctestingtypes.StakingKeeper { + return app.StakingKeeper +} + +// GetIBCKeeper implements the TestingApp interface. +func (app *SimApp) GetIBCKeeper() *ibckeeper.Keeper { + return app.IBCKeeper +} + +// GetScopedIBCKeeper implements the TestingApp interface. +func (app *SimApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { + return app.ScopedIBCKeeper +} + +// GetTxConfig implements the TestingApp interface. +func (app *SimApp) GetTxConfig() client.TxConfig { + return app.txConfig +} diff --git a/modules/apps/callbacks/testing/simapp/contract_keeper.go b/modules/apps/callbacks/testing/simapp/contract_keeper.go new file mode 100644 index 00000000000..cd69486df0c --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/contract_keeper.go @@ -0,0 +1,160 @@ +package simapp + +import ( + "fmt" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + callbacktypes "github.com/cosmos/ibc-go/v7/modules/apps/callbacks/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" +) + +// MockKeeper implements callbacktypes.ContractKeeper +var _ callbacktypes.ContractKeeper = (*ContractKeeper)(nil) + +var ( + StatefulCounterKey = "stateful-callback-counter" + MockCallbackUnauthorizedAddress = "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk" +) + +// This is a mock contract keeper used for testing. It is not wired up to any modules. +// It implements the interface functions expected by the ibccallbacks middleware +// so that it can be tested with simapp. The keeper is responsible for tracking +// two metrics: +// - number of callbacks called per callback type +// - stateful entry attempts +// +// The counter for callbacks allows us to ensure the correct callbacks were routed to +// and the stateful entries allows us to track state reversals or reverted state upon +// contract execution failure or out of gas errors. +type ContractKeeper struct { + key storetypes.StoreKey + + Counters map[callbacktypes.CallbackType]int +} + +// SetStateEntryCounter sets state entry counter. The number of stateful +// entries is tracked as a uint8. This function is used to test state reversals. +func (k ContractKeeper) SetStateEntryCounter(ctx sdk.Context, count uint8) { + store := ctx.KVStore(k.key) + store.Set([]byte(StatefulCounterKey), []byte{count}) +} + +// GetStateEntryCounter returns the state entry counter stored in state. +func (k ContractKeeper) GetStateEntryCounter(ctx sdk.Context) uint8 { + store := ctx.KVStore(k.key) + bz := store.Get([]byte(StatefulCounterKey)) + if bz == nil { + return 0 + } + return bz[0] +} + +// IncrementStatefulCounter increments the stateful callback counter in state. +func (k ContractKeeper) IncrementStateEntryCounter(ctx sdk.Context) { + count := k.GetStateEntryCounter(ctx) + k.SetStateEntryCounter(ctx, count+1) +} + +// NewKeeper creates a new mock ContractKeeper. +func NewContractKeeper(key storetypes.StoreKey) ContractKeeper { + return ContractKeeper{ + key: key, + Counters: make(map[callbacktypes.CallbackType]int), + } +} + +// IBCPacketSendCallback returns nil if the gas meter has greater than +// or equal to 500_000 gas remaining. +// This function oog panics if the gas remaining is less than 500_000. +// This function errors if the authAddress is MockCallbackUnauthorizedAddress. +func (k ContractKeeper) IBCSendPacketCallback( + ctx sdk.Context, + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + packetData []byte, + contractAddress, + packetSenderAddress string, +) error { + return k.processMockCallback(ctx, callbacktypes.CallbackTypeSendPacket, packetSenderAddress) +} + +// IBCOnAcknowledgementPacketCallback returns nil if the gas meter has greater than +// or equal to 500_000 gas remaining. +// This function oog panics if the gas remaining is less than 500_000. +// This function errors if the authAddress is MockCallbackUnauthorizedAddress. +func (k ContractKeeper) IBCOnAcknowledgementPacketCallback( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, + contractAddress, + packetSenderAddress string, +) error { + return k.processMockCallback(ctx, callbacktypes.CallbackTypeAcknowledgementPacket, packetSenderAddress) +} + +// IBCOnTimeoutPacketCallback returns nil if the gas meter has greater than +// or equal to 500_000 gas remaining. +// This function oog panics if the gas remaining is less than 500_000. +// This function errors if the authAddress is MockCallbackUnauthorizedAddress. +func (k ContractKeeper) IBCOnTimeoutPacketCallback( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, + contractAddress, + packetSenderAddress string, +) error { + return k.processMockCallback(ctx, callbacktypes.CallbackTypeTimeoutPacket, packetSenderAddress) +} + +// IBCReceivePacketCallback returns nil if the gas meter has greater than +// or equal to 500_000 gas remaining. +// This function oog panics if the gas remaining is less than 500_000. +// This function errors if the authAddress is MockCallbackUnauthorizedAddress. +func (k ContractKeeper) IBCReceivePacketCallback( + ctx sdk.Context, + packet ibcexported.PacketI, + ack ibcexported.Acknowledgement, + contractAddress string, +) error { + return k.processMockCallback(ctx, callbacktypes.CallbackTypeReceivePacket, "") +} + +// processMockCallback returns nil if the gas meter has greater than or equal to 500_000 gas remaining. +// This function oog panics if the gas remaining is less than 500_000. +// This function errors if the authAddress is MockCallbackUnauthorizedAddress. +func (k ContractKeeper) processMockCallback( + ctx sdk.Context, + callbackType callbacktypes.CallbackType, + authAddress string, +) error { + gasRemaining := ctx.GasMeter().GasRemaining() + + // increment stateful entries, if the callbacks module handler + // reverts state, we can check by querying for the counter + // currently stored. + k.IncrementStateEntryCounter(ctx) + + // increment callback execution attempts + k.Counters[callbackType]++ + + if gasRemaining < 500000 { + // consume gas will panic since we attempt to consume 500_000 gas, for tests + ctx.GasMeter().ConsumeGas(500000, fmt.Sprintf("mock %s callback panic", callbackType)) + } + + if authAddress == MockCallbackUnauthorizedAddress { + ctx.GasMeter().ConsumeGas(500000, fmt.Sprintf("mock %s callback unauthorized", callbackType)) + return ibcmock.MockApplicationCallbackError + } + + ctx.GasMeter().ConsumeGas(500000, fmt.Sprintf("mock %s callback success", callbackType)) + return nil +} diff --git a/modules/apps/callbacks/testing/simapp/encoding.go b/modules/apps/callbacks/testing/simapp/encoding.go new file mode 100644 index 00000000000..9f78e73e3c7 --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/encoding.go @@ -0,0 +1,20 @@ +package simapp + +import ( + "github.com/cosmos/cosmos-sdk/std" + + simappparams "github.com/cosmos/ibc-go/v7/modules/apps/callbacks/testing/simapp/params" +) + +// MakeTestEncodingConfig creates an EncodingConfig for testing. This function +// should be used only in tests or when creating a new app instance (NewApp*()). +// App user shouldn't create new codecs - use the app.AppCodec instead. +// [DEPRECATED] +func MakeTestEncodingConfig() simappparams.EncodingConfig { + encodingConfig := simappparams.MakeTestEncodingConfig() + std.RegisterLegacyAminoCodec(encodingConfig.Amino) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) + ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) + return encodingConfig +} diff --git a/modules/apps/callbacks/testing/simapp/export.go b/modules/apps/callbacks/testing/simapp/export.go new file mode 100644 index 00000000000..4d6489f63d4 --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/export.go @@ -0,0 +1,200 @@ +package simapp + +import ( + "encoding/json" + "log" + + servertypes "github.com/cosmos/cosmos-sdk/server/types" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" +) + +// ExportAppStateAndValidators exports the state of the application for a genesis +// file. +func (app *SimApp) ExportAppStateAndValidators( + forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string, +) (servertypes.ExportedApp, error) { + // as if they could withdraw from the start of the next block + ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + + // We export at last height + 1, because that's the height at which + // Tendermint will start InitChain. + height := app.LastBlockHeight() + 1 + if forZeroHeight { + height = 0 + app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) + } + + genState := app.ModuleManager.ExportGenesis(ctx, app.appCodec) + appState, err := json.MarshalIndent(genState, "", " ") + if err != nil { + return servertypes.ExportedApp{}, err + } + + validators, err := staking.WriteValidators(ctx, app.StakingKeeper) + return servertypes.ExportedApp{ + AppState: appState, + Validators: validators, + Height: height, + ConsensusParams: app.BaseApp.GetConsensusParams(ctx), + }, err +} + +// prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// in favour of export at a block height +func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { + applyAllowedAddrs := false + + // check if there is a allowed address list + if len(jailAllowedAddrs) > 0 { + applyAllowedAddrs = true + } + + allowedAddrsMap := make(map[string]bool) + + for _, addr := range jailAllowedAddrs { + _, err := sdk.ValAddressFromBech32(addr) + if err != nil { + log.Fatal(err) + } + allowedAddrsMap[addr] = true + } + + /* Just to be safe, assert the invariants on current state. */ + app.CrisisKeeper.AssertInvariants(ctx) + + /* Handle fee distribution state. */ + + // withdraw all validator commission + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) + return false + }) + + // withdraw all delegator rewards + dels := app.StakingKeeper.GetAllDelegations(ctx) + for _, delegation := range dels { + valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) + if err != nil { + panic(err) + } + + delAddr, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) + if err != nil { + panic(err) + } + _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr) + } + + // clear validator slash events + app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) + + // clear validator historical rewards + app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + // set context height to zero + height := ctx.BlockHeight() + ctx = ctx.WithBlockHeight(0) + + // reinitialize all validators + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + // donate any unwithdrawn outstanding reward fraction tokens to the community pool + scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) + feePool := app.DistrKeeper.GetFeePool(ctx) + feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) + app.DistrKeeper.SetFeePool(ctx, feePool) + + err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) + return err != nil + }) + + // reinitialize all delegations + for _, del := range dels { + valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) + if err != nil { + panic(err) + } + delAddr, err := sdk.AccAddressFromBech32(del.DelegatorAddress) + if err != nil { + panic(err) + } + err = app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr) + if err != nil { + panic(err) + } + err = app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr) + if err != nil { + panic(err) + } + } + + // reset context height + ctx = ctx.WithBlockHeight(height) + + /* Handle staking state. */ + + // iterate through redelegations, reset creation height + app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { + for i := range red.Entries { + red.Entries[i].CreationHeight = 0 + } + app.StakingKeeper.SetRedelegation(ctx, red) + return false + }) + + // iterate through unbonding delegations, reset creation height + app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { + for i := range ubd.Entries { + ubd.Entries[i].CreationHeight = 0 + } + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + return false + }) + + // Iterate through validators by power descending, reset bond heights, and + // update bond intra-tx counters. + store := ctx.KVStore(app.keys[stakingtypes.StoreKey]) + iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) + counter := int16(0) + + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) + validator, found := app.StakingKeeper.GetValidator(ctx, addr) + if !found { + panic("expected validator, not found") + } + + validator.UnbondingHeight = 0 + if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { + validator.Jailed = true + } + + app.StakingKeeper.SetValidator(ctx, validator) + counter++ + } + + iter.Close() + + _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + log.Fatal(err) + } + + /* Handle slashing state. */ + + // reset start height on signing infos + app.SlashingKeeper.IterateValidatorSigningInfos( + ctx, + func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { + info.StartHeight = 0 + app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) + return false + }, + ) +} diff --git a/modules/apps/callbacks/testing/simapp/genesis.go b/modules/apps/callbacks/testing/simapp/genesis.go new file mode 100644 index 00000000000..772e452d443 --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/genesis.go @@ -0,0 +1,21 @@ +package simapp + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// The genesis state of the blockchain is represented here as a map of raw json +// messages key'd by a identifier string. +// The identifier is used to determine which module genesis information belongs +// to so it may be appropriately routed during init chain. +// Within this application default genesis information is retrieved from +// the ModuleBasicManager which populates json from each BasicModule +// object provided to it during init. +type GenesisState map[string]json.RawMessage + +// NewDefaultGenesisState generates the default state for the application. +func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { + return ModuleBasics.DefaultGenesis(cdc) +} diff --git a/modules/apps/callbacks/testing/simapp/genesis_account.go b/modules/apps/callbacks/testing/simapp/genesis_account.go new file mode 100644 index 00000000000..5c9c7f9a03f --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/genesis_account.go @@ -0,0 +1,47 @@ +package simapp + +import ( + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var _ authtypes.GenesisAccount = (*SimGenesisAccount)(nil) + +// SimGenesisAccount defines a type that implements the GenesisAccount interface +// to be used for simulation accounts in the genesis state. +type SimGenesisAccount struct { + *authtypes.BaseAccount + + // vesting account fields + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` // total vesting coins upon initialization + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` // delegated vested coins at time of delegation + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` // delegated vesting coins at time of delegation + StartTime int64 `json:"start_time" yaml:"start_time"` // vesting start time (UNIX Epoch time) + EndTime int64 `json:"end_time" yaml:"end_time"` // vesting end time (UNIX Epoch time) + + // module account fields + ModuleName string `json:"module_name" yaml:"module_name"` // name of the module account + ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"` // permissions of module account +} + +// Validate checks for errors on the vesting and module account parameters +func (sga SimGenesisAccount) Validate() error { + if !sga.OriginalVesting.IsZero() { + if sga.StartTime >= sga.EndTime { + return errors.New("vesting start-time cannot be before end-time") + } + } + + if sga.ModuleName != "" { + ma := authtypes.ModuleAccount{ + BaseAccount: sga.BaseAccount, Name: sga.ModuleName, Permissions: sga.ModulePermissions, + } + if err := ma.Validate(); err != nil { + return err + } + } + + return sga.BaseAccount.Validate() +} diff --git a/modules/apps/callbacks/testing/simapp/params/amino.go b/modules/apps/callbacks/testing/simapp/params/amino.go new file mode 100644 index 00000000000..c7d8ba0045d --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/params/amino.go @@ -0,0 +1,27 @@ +//go:build test_amino +// +build test_amino + +package params + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" +) + +// MakeTestEncodingConfig creates an EncodingConfig for an amino based test configuration. +// This function should be used only internally (in the SDK). +// App user shouldn't create new codecs - use the app.AppCodec instead. +// [DEPRECATED] +func MakeTestEncodingConfig() EncodingConfig { + cdc := codec.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewAminoCodec(cdc) + + return EncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Marshaler: marshaler, + TxConfig: legacytx.StdTxConfig{Cdc: cdc}, + Amino: cdc, + } +} diff --git a/modules/apps/callbacks/testing/simapp/params/doc.go b/modules/apps/callbacks/testing/simapp/params/doc.go new file mode 100644 index 00000000000..a2f3620a835 --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/params/doc.go @@ -0,0 +1,19 @@ +/* +Package params defines the simulation parameters in the simapp. + +It contains the default weights used for each transaction used on the module's +simulation. These weights define the chance for a transaction to be simulated at +any given operation. + +You can replace the default values for the weights by providing a params.json +file with the weights defined for each of the transaction operations: + + { + "op_weight_msg_send": 60, + "op_weight_msg_delegate": 100, + } + +In the example above, the `MsgSend` has 60% chance to be simulated, while the +`MsgDelegate` will always be simulated. +*/ +package params diff --git a/modules/apps/callbacks/testing/simapp/params/encoding.go b/modules/apps/callbacks/testing/simapp/params/encoding.go new file mode 100644 index 00000000000..8ff9ea04b39 --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/params/encoding.go @@ -0,0 +1,16 @@ +package params + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" +) + +// EncodingConfig specifies the concrete encoding types to use for a given app. +// This is provided for compatibility between protobuf and amino implementations. +type EncodingConfig struct { + InterfaceRegistry types.InterfaceRegistry + Codec codec.Codec + TxConfig client.TxConfig + Amino *codec.LegacyAmino +} diff --git a/modules/apps/callbacks/testing/simapp/params/proto.go b/modules/apps/callbacks/testing/simapp/params/proto.go new file mode 100644 index 00000000000..4270c2b0dbf --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/params/proto.go @@ -0,0 +1,27 @@ +//go:build !test_amino +// +build !test_amino + +package params + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/auth/tx" +) + +// MakeTestEncodingConfig creates an EncodingConfig for a non-amino based test configuration. +// This function should be used only internally (in the SDK). +// App user should'nt create new codecs - use the app.AppCodec instead. +// [DEPRECATED] +func MakeTestEncodingConfig() EncodingConfig { + cdc := codec.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + protoCdc := codec.NewProtoCodec(interfaceRegistry) + + return EncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Codec: protoCdc, + TxConfig: tx.NewTxConfig(protoCdc, tx.DefaultSignModes), + Amino: cdc, + } +} diff --git a/modules/apps/callbacks/transfer_test.go b/modules/apps/callbacks/transfer_test.go index bfe55691216..76874fc1d5d 100644 --- a/modules/apps/callbacks/transfer_test.go +++ b/modules/apps/callbacks/transfer_test.go @@ -148,10 +148,10 @@ func (s *CallbacksTestSuite) TestTransferTimeoutCallbacks() { func (s *CallbacksTestSuite) ExecuteTransfer(memo string) { escrowAddress := transfertypes.GetEscrowAddress(s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID) // record the balance of the escrow address before the transfer - escrowBalance := s.chainA.GetSimApp().BankKeeper.GetBalance(s.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom) + escrowBalance := GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom) // record the balance of the receiving address before the transfer voucherDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(s.path.EndpointB.ChannelConfig.PortID, s.path.EndpointB.ChannelID, sdk.DefaultBondDenom)) - receiverBalance := s.chainB.GetSimApp().BankKeeper.GetBalance(s.chainB.GetContext(), s.chainB.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom()) + receiverBalance := GetSimApp(s.chainB).BankKeeper.GetBalance(s.chainB.GetContext(), s.chainB.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom()) amount := ibctesting.TestCoin msg := transfertypes.NewMsgTransfer( @@ -176,9 +176,9 @@ func (s *CallbacksTestSuite) ExecuteTransfer(memo string) { s.Require().NoError(err) // relay committed // check that the escrow address balance increased by 100 - s.Require().Equal(escrowBalance.Add(amount), s.chainA.GetSimApp().BankKeeper.GetBalance(s.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom)) + s.Require().Equal(escrowBalance.Add(amount), GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom)) // check that the receiving address balance increased by 100 - s.Require().Equal(receiverBalance.AddAmount(sdk.NewInt(100)), s.chainB.GetSimApp().BankKeeper.GetBalance(s.chainB.GetContext(), s.chainB.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom())) + s.Require().Equal(receiverBalance.AddAmount(sdk.NewInt(100)), GetSimApp(s.chainB).BankKeeper.GetBalance(s.chainB.GetContext(), s.chainB.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom())) } // ExecuteTransferTimeout executes a transfer message on chainA for 100 denom.