github.com/cosmos/cosmos-sdk@v0.50.10/testutil/sims/app_helpers.go (about) 1 package sims 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "time" 7 8 abci "github.com/cometbft/cometbft/abci/types" 9 cmtjson "github.com/cometbft/cometbft/libs/json" 10 cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 11 cmttypes "github.com/cometbft/cometbft/types" 12 dbm "github.com/cosmos/cosmos-db" 13 14 coreheader "cosmossdk.io/core/header" 15 "cosmossdk.io/depinject" 16 sdkmath "cosmossdk.io/math" 17 18 "github.com/cosmos/cosmos-sdk/client/flags" 19 "github.com/cosmos/cosmos-sdk/codec" 20 codectypes "github.com/cosmos/cosmos-sdk/codec/types" 21 cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 22 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" 23 "github.com/cosmos/cosmos-sdk/runtime" 24 servertypes "github.com/cosmos/cosmos-sdk/server/types" 25 "github.com/cosmos/cosmos-sdk/testutil/mock" 26 sdk "github.com/cosmos/cosmos-sdk/types" 27 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 28 banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 29 stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 30 ) 31 32 const DefaultGenTxGas = 10000000 33 34 // DefaultConsensusParams defines the default CometBFT consensus params used in 35 // SimApp testing. 36 var DefaultConsensusParams = &cmtproto.ConsensusParams{ 37 Block: &cmtproto.BlockParams{ 38 MaxBytes: 200000, 39 MaxGas: 100_000_000, 40 }, 41 Evidence: &cmtproto.EvidenceParams{ 42 MaxAgeNumBlocks: 302400, 43 MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration 44 MaxBytes: 10000, 45 }, 46 Validator: &cmtproto.ValidatorParams{ 47 PubKeyTypes: []string{ 48 cmttypes.ABCIPubKeyTypeEd25519, 49 }, 50 }, 51 } 52 53 // CreateRandomValidatorSet creates a validator set with one random validator 54 func CreateRandomValidatorSet() (*cmttypes.ValidatorSet, error) { 55 privVal := mock.NewPV() 56 pubKey, err := privVal.GetPubKey() 57 if err != nil { 58 return nil, fmt.Errorf("failed to get pub key: %w", err) 59 } 60 61 // create validator set with single validator 62 validator := cmttypes.NewValidator(pubKey, 1) 63 64 return cmttypes.NewValidatorSet([]*cmttypes.Validator{validator}), nil 65 } 66 67 type GenesisAccount struct { 68 authtypes.GenesisAccount 69 Coins sdk.Coins 70 } 71 72 // StartupConfig defines the startup configuration new a test application. 73 // 74 // ValidatorSet defines a custom validator set to be validating the app. 75 // BaseAppOption defines the additional operations that must be run on baseapp before app start. 76 // AtGenesis defines if the app started should already have produced block or not. 77 type StartupConfig struct { 78 ValidatorSet func() (*cmttypes.ValidatorSet, error) 79 BaseAppOption runtime.BaseAppOption 80 AtGenesis bool 81 GenesisAccounts []GenesisAccount 82 DB dbm.DB 83 } 84 85 func DefaultStartUpConfig() StartupConfig { 86 priv := secp256k1.GenPrivKey() 87 ba := authtypes.NewBaseAccount(priv.PubKey().Address().Bytes(), priv.PubKey(), 0, 0) 88 ga := GenesisAccount{ba, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100000000000000)))} 89 return StartupConfig{ 90 ValidatorSet: CreateRandomValidatorSet, 91 AtGenesis: false, 92 GenesisAccounts: []GenesisAccount{ga}, 93 DB: dbm.NewMemDB(), 94 } 95 } 96 97 // Setup initializes a new runtime.App and can inject values into extraOutputs. 98 // It uses SetupWithConfiguration under the hood. 99 func Setup(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { 100 return SetupWithConfiguration(appConfig, DefaultStartUpConfig(), extraOutputs...) 101 } 102 103 // SetupAtGenesis initializes a new runtime.App at genesis and can inject values into extraOutputs. 104 // It uses SetupWithConfiguration under the hood. 105 func SetupAtGenesis(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { 106 cfg := DefaultStartUpConfig() 107 cfg.AtGenesis = true 108 return SetupWithConfiguration(appConfig, cfg, extraOutputs...) 109 } 110 111 // NextBlock starts a new block. 112 func NextBlock(app *runtime.App, ctx sdk.Context, jumpTime time.Duration) (sdk.Context, error) { 113 _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: ctx.BlockHeight(), Time: ctx.BlockTime()}) 114 if err != nil { 115 return sdk.Context{}, err 116 } 117 _, err = app.Commit() 118 if err != nil { 119 return sdk.Context{}, err 120 } 121 122 newBlockTime := ctx.BlockTime().Add(jumpTime) 123 124 header := ctx.BlockHeader() 125 header.Time = newBlockTime 126 header.Height++ 127 128 newCtx := app.BaseApp.NewUncachedContext(false, header).WithHeaderInfo(coreheader.Info{ 129 Height: header.Height, 130 Time: header.Time, 131 }) 132 133 if err != nil { 134 return sdk.Context{}, err 135 } 136 137 return newCtx, err 138 } 139 140 // SetupWithConfiguration initializes a new runtime.App. A Nop logger is set in runtime.App. 141 // appConfig defines the application configuration (f.e. app_config.go). 142 // extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject). 143 func SetupWithConfiguration(appConfig depinject.Config, startupConfig StartupConfig, extraOutputs ...interface{}) (*runtime.App, error) { 144 // create the app with depinject 145 var ( 146 app *runtime.App 147 appBuilder *runtime.AppBuilder 148 codec codec.Codec 149 ) 150 151 if err := depinject.Inject(appConfig, append(extraOutputs, &appBuilder, &codec)...); err != nil { 152 return nil, fmt.Errorf("failed to inject dependencies: %w", err) 153 } 154 155 if startupConfig.BaseAppOption != nil { 156 app = appBuilder.Build(startupConfig.DB, nil, startupConfig.BaseAppOption) 157 } else { 158 app = appBuilder.Build(startupConfig.DB, nil) 159 } 160 if err := app.Load(true); err != nil { 161 return nil, fmt.Errorf("failed to load app: %w", err) 162 } 163 164 // create validator set 165 valSet, err := startupConfig.ValidatorSet() 166 if err != nil { 167 return nil, fmt.Errorf("failed to create validator set") 168 } 169 170 var ( 171 balances []banktypes.Balance 172 genAccounts []authtypes.GenesisAccount 173 ) 174 for _, ga := range startupConfig.GenesisAccounts { 175 genAccounts = append(genAccounts, ga.GenesisAccount) 176 balances = append(balances, banktypes.Balance{Address: ga.GenesisAccount.GetAddress().String(), Coins: ga.Coins}) 177 } 178 179 genesisState, err := GenesisStateWithValSet(codec, app.DefaultGenesis(), valSet, genAccounts, balances...) 180 if err != nil { 181 return nil, fmt.Errorf("failed to create genesis state: %w", err) 182 } 183 184 // init chain must be called to stop deliverState from being nil 185 stateBytes, err := cmtjson.MarshalIndent(genesisState, "", " ") 186 if err != nil { 187 return nil, fmt.Errorf("failed to marshal default genesis state: %w", err) 188 } 189 190 // init chain will set the validator set and initialize the genesis accounts 191 _, err = app.InitChain(&abci.RequestInitChain{ 192 Validators: []abci.ValidatorUpdate{}, 193 ConsensusParams: DefaultConsensusParams, 194 AppStateBytes: stateBytes, 195 }) 196 if err != nil { 197 return nil, fmt.Errorf("failed to init chain: %w", err) 198 } 199 200 // commit genesis changes 201 if !startupConfig.AtGenesis { 202 _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{ 203 Height: app.LastBlockHeight() + 1, 204 NextValidatorsHash: valSet.Hash(), 205 }) 206 if err != nil { 207 return nil, fmt.Errorf("failed to finalize block: %w", err) 208 } 209 } 210 211 return app, nil 212 } 213 214 // GenesisStateWithValSet returns a new genesis state with the validator set 215 func GenesisStateWithValSet( 216 codec codec.Codec, 217 genesisState map[string]json.RawMessage, 218 valSet *cmttypes.ValidatorSet, 219 genAccs []authtypes.GenesisAccount, 220 balances ...banktypes.Balance, 221 ) (map[string]json.RawMessage, error) { 222 // set genesis accounts 223 authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) 224 genesisState[authtypes.ModuleName] = codec.MustMarshalJSON(authGenesis) 225 226 validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) 227 delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) 228 229 bondAmt := sdk.DefaultPowerReduction 230 231 for _, val := range valSet.Validators { 232 pk, err := cryptocodec.FromCmtPubKeyInterface(val.PubKey) 233 if err != nil { 234 return nil, fmt.Errorf("failed to convert pubkey: %w", err) 235 } 236 237 pkAny, err := codectypes.NewAnyWithValue(pk) 238 if err != nil { 239 return nil, fmt.Errorf("failed to create new any: %w", err) 240 } 241 242 validator := stakingtypes.Validator{ 243 OperatorAddress: sdk.ValAddress(val.Address).String(), 244 ConsensusPubkey: pkAny, 245 Jailed: false, 246 Status: stakingtypes.Bonded, 247 Tokens: bondAmt, 248 DelegatorShares: sdkmath.LegacyOneDec(), 249 Description: stakingtypes.Description{}, 250 UnbondingHeight: int64(0), 251 UnbondingTime: time.Unix(0, 0).UTC(), 252 Commission: stakingtypes.NewCommission(sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec()), 253 MinSelfDelegation: sdkmath.ZeroInt(), 254 } 255 validators = append(validators, validator) 256 delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress().String(), sdk.ValAddress(val.Address).String(), sdkmath.LegacyOneDec())) 257 258 } 259 260 // set validators and delegations 261 stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) 262 genesisState[stakingtypes.ModuleName] = codec.MustMarshalJSON(stakingGenesis) 263 264 totalSupply := sdk.NewCoins() 265 for _, b := range balances { 266 // add genesis acc tokens to total supply 267 totalSupply = totalSupply.Add(b.Coins...) 268 } 269 270 for range delegations { 271 // add delegated tokens to total supply 272 totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) 273 } 274 275 // add bonded amount to bonded pool module account 276 balances = append(balances, banktypes.Balance{ 277 Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), 278 Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, 279 }) 280 281 // update total supply 282 bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}, []banktypes.SendEnabled{}) 283 genesisState[banktypes.ModuleName] = codec.MustMarshalJSON(bankGenesis) 284 285 return genesisState, nil 286 } 287 288 // EmptyAppOptions is a stub implementing AppOptions 289 type EmptyAppOptions struct{} 290 291 // Get implements AppOptions 292 func (ao EmptyAppOptions) Get(o string) interface{} { 293 return nil 294 } 295 296 // AppOptionsMap is a stub implementing AppOptions which can get data from a map 297 type AppOptionsMap map[string]interface{} 298 299 func (m AppOptionsMap) Get(key string) interface{} { 300 v, ok := m[key] 301 if !ok { 302 return interface{}(nil) 303 } 304 305 return v 306 } 307 308 func NewAppOptionsWithFlagHome(homePath string) servertypes.AppOptions { 309 return AppOptionsMap{ 310 flags.FlagHome: homePath, 311 } 312 }