package simapp import ( "encoding/json" "testing" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" "cosmossdk.io/depinject" "cosmossdk.io/math" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/testutil/mock" "github.com/cosmos/cosmos-sdk/testutil/network" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module/testutil" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" ) // SetupOptions defines arguments that are passed into `Simapp` constructor. type SetupOptions struct { Logger log.Logger DB *dbm.MemDB AppOpts servertypes.AppOptions } func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) { db := dbm.NewMemDB() appOptions := make(simtestutil.AppOptionsMap, 0) appOptions[flags.FlagHome] = DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = invCheckPeriod app := NewSimApp(log.NewNopLogger(), db, nil, true, appOptions) if withGenesis { return app, NewDefaultGenesisState(app.AppCodec()) } return app, GenesisState{} } // NewSimappWithCustomOptions initializes a new SimApp with custom options. func NewSimappWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptions) *SimApp { t.Helper() privVal := mock.NewPV() pubKey, err := privVal.GetPubKey() require.NoError(t, err) // create validator set with single validator validator := tmtypes.NewValidator(pubKey, 1) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) // generate genesis account senderPrivKey := secp256k1.GenPrivKey() acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) balance := banktypes.Balance{ Address: acc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), } app := NewSimApp(options.Logger, options.DB, nil, true, options.AppOpts) genesisState := NewDefaultGenesisState(app.appCodec) genesisState, err = simtestutil.GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) require.NoError(t, err) if !isCheckTx { // init chain must be called to stop deliverState from being nil stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") require.NoError(t, err) // Initialize the chain app.InitChain( abci.RequestInitChain{ Validators: []abci.ValidatorUpdate{}, ConsensusParams: simtestutil.DefaultConsensusParams, AppStateBytes: stateBytes, }, ) } return app } // Setup initializes a new SimApp. A Nop logger is set in SimApp. func Setup(t *testing.T, isCheckTx bool) *SimApp { t.Helper() privVal := mock.NewPV() pubKey, err := privVal.GetPubKey() require.NoError(t, err) // create validator set with single validator validator := tmtypes.NewValidator(pubKey, 1) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) // generate genesis account senderPrivKey := secp256k1.GenPrivKey() acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) balance := banktypes.Balance{ Address: acc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), } app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) return app } // SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts // that also act as delegators. For simplicity, each validator is bonded with a delegation // of one consensus engine unit in the default token of the simapp from first genesis // account. A Nop logger is set in SimApp. func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { t.Helper() app, genesisState := setup(true, 5) genesisState, err := simtestutil.GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, genAccs, balances...) require.NoError(t, err) stateBytes, err := json.MarshalIndent(genesisState, "", " ") require.NoError(t, err) // init chain will set the validator set and initialize the genesis accounts app.InitChain( abci.RequestInitChain{ Validators: []abci.ValidatorUpdate{}, ConsensusParams: simtestutil.DefaultConsensusParams, AppStateBytes: stateBytes, }, ) // commit genesis changes app.Commit() app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash, ValidatorsHash: valSet.Hash(), NextValidatorsHash: valSet.Hash(), }}) return app } // SetupWithGenesisAccounts initializes a new SimApp with the provided genesis // accounts and possible balances. func SetupWithGenesisAccounts(t *testing.T, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { t.Helper() privVal := mock.NewPV() pubKey, err := privVal.GetPubKey() require.NoError(t, err) // create validator set with single validator validator := tmtypes.NewValidator(pubKey, 1) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) return SetupWithGenesisValSet(t, valSet, genAccs, balances...) } // GenesisStateWithSingleValidator initializes GenesisState with a single validator and genesis accounts // that also act as delegators. func GenesisStateWithSingleValidator(t *testing.T, app *SimApp) GenesisState { t.Helper() privVal := mock.NewPV() pubKey, err := privVal.GetPubKey() require.NoError(t, err) // create validator set with single validator validator := tmtypes.NewValidator(pubKey, 1) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) // generate genesis account senderPrivKey := secp256k1.GenPrivKey() acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) balances := []banktypes.Balance{ { Address: acc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), }, } genesisState := NewDefaultGenesisState(app.appCodec) genesisState, err = simtestutil.GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balances...) require.NoError(t, err) return genesisState } // AddTestAddrsFromPubKeys adds the addresses into the SimApp providing only the public keys. func AddTestAddrsFromPubKeys(app *SimApp, ctx sdk.Context, pubKeys []cryptotypes.PubKey, accAmt math.Int) { initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) for _, pk := range pubKeys { initAccountWithCoins(app, ctx, sdk.AccAddress(pk.Address()), initCoins) } } // AddTestAddrs constructs and returns accNum amount of accounts with an // initial balance of accAmt in random order func AddTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt math.Int) []sdk.AccAddress { return addTestAddrs(app, ctx, accNum, accAmt, simtestutil.CreateRandomAccounts) } // AddTestAddrsIncremental constructs and returns accNum amount of accounts with an // initial balance of accAmt in random order func AddTestAddrsIncremental(app *SimApp, ctx sdk.Context, accNum int, accAmt math.Int) []sdk.AccAddress { return addTestAddrs(app, ctx, accNum, accAmt, simtestutil.CreateIncrementalAccounts) } func addTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt math.Int, strategy simtestutil.GenerateAccountStrategy) []sdk.AccAddress { testAddrs := strategy(accNum) initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) for _, addr := range testAddrs { initAccountWithCoins(app, ctx, addr, initCoins) } return testAddrs } func initAccountWithCoins(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins) if err != nil { panic(err) } err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, coins) if err != nil { panic(err) } } // CheckBalance checks the balance of an account. func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.Coins) { ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) require.True(t, balances.IsEqual(app.BankKeeper.GetAllBalances(ctxCheck, addr))) } // ModuleAccountAddrs provides a list of blocked module accounts from configuration in app.yaml // // Ported from SimApp func ModuleAccountAddrs() map[string]bool { var bk bankkeeper.Keeper err := depinject.Inject(AppConfig, &bk) if err != nil { panic("unable to load DI container") } return bk.GetBlockedAddresses() } // NewTestNetworkFixture returns a new simapp AppConstructor for network simulation tests func NewTestNetworkFixture() network.TestFixture { app := NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simtestutil.EmptyAppOptions{}) appCtr := func(val testutil.Validator) servertypes.Application { return NewSimApp( val.GetCtx().Logger, dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(val.GetCtx().Config.RootDir), bam.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), bam.SetMinGasPrices(val.GetAppConfig().MinGasPrices), ) } return network.TestFixture{ AppConstructor: appCtr, GenesisState: ModuleBasics.DefaultGenesis(app.AppCodec()), EncodingConfig: testutil.TestEncodingConfig{ InterfaceRegistry: app.InterfaceRegistry(), Codec: app.AppCodec(), TxConfig: app.TxConfig(), Amino: app.LegacyAmino(), }, } }