diff --git a/doc.go b/doc.go new file mode 100644 index 000000000000..ee76bca1ece2 --- /dev/null +++ b/doc.go @@ -0,0 +1,93 @@ +/* +Package simapp implements a full fledged Cosmos SDK application used for executing +simulation test suites. + +Simulation App + +The SimApp type defines an application used for running extensive simulation +testing suites. It contains all core modules, including governance, staking, +slashing, and distribution. + +Simulation is executed with various inputs including the number of blocks to +simulate, the block size, whether the app should commit or not, the invariant +checking period, and a seed which is used as a source of pseudo-randomness. + +In addition to the various inputs, simulation runs mainly in three modes: + +1. Completely random where the initial state, module parameters and simulation +parameters are pseudo-randomly generated. + +2. From a genesis file where the initial state and the module parameters are defined. +This mode is helpful for running simulations on a known state such as a live +network export where a new (mostly likely breaking) version of the application +needs to be tested. + +3. From a params file where the initial state is pseudo-randomly generated but the +module and simulation parameters can be provided manually. This allows for a more +controlled and deterministic simulation setup while allowing the state space to +still be pseudo-randomly simulated. + +The simulation test suite also supports testing determinism and import/export +functionality. + +Randomness + +Currently, simulation uses a single seed (integer) as a source for a PRNG by +which all random operations are executed from. Any call to the PRNG changes all +future operations as the internal state of the PRNG is modified. For example, +if a new message type is created and needs to be simulated, the new introduced +PRNG call will change all subsequent operations. + +This may can often be problematic when testing fixes to simulation faults. One +current solution to this is to use a params file as mentioned above. In the future +the simulation suite is expected to support a series of PRNGs that can be used +uniquely per module and simulation component so that they will not effect each +others state execution outcome. + +Usage + +To execute a completely pseudo-random simulation: + + $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ + -run=TestFullAppSimulation \ + -SimulationEnabled=true \ + -SimulationNumBlocks=100 \ + -SimulationBlockSize=200 \ + -SimulationCommit=true \ + -SimulationSeed=99 \ + -SimulationPeriod=5 \ + -v -timeout 24h + +To execute simulation from a genesis file: + + $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ + -run=TestFullAppSimulation \ + -SimulationEnabled=true \ + -SimulationNumBlocks=100 \ + -SimulationBlockSize=200 \ + -SimulationCommit=true \ + -SimulationSeed=99 \ + -SimulationPeriod=5 \ + -SimulationGenesis=/path/to/genesis.json \ + -v -timeout 24h + +To execute simulation from a params file: + + $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ + -run=TestFullAppSimulation \ + -SimulationEnabled=true \ + -SimulationNumBlocks=100 \ + -SimulationBlockSize=200 \ + -SimulationCommit=true \ + -SimulationSeed=99 \ + -SimulationPeriod=5 \ + -SimulationParams=/path/to/params.json \ + -v -timeout 24h + +Params + +Params that are provided to simulation from a JSON file are used to used to set +both module parameters and simulation parameters. See sim_test.go for the full +set of parameters that can be provided. +*/ +package simapp diff --git a/params.go b/params.go new file mode 100644 index 000000000000..b081715f3773 --- /dev/null +++ b/params.go @@ -0,0 +1,23 @@ +package simapp + +// Simulation parameter constants +const ( + StakePerAccount = "stake_per_account" + InitiallyBondedValidators = "initially_bonded_validators" + OpWeightDeductFee = "op_weight_deduct_fee" + OpWeightMsgSend = "op_weight_msg_send" + OpWeightSingleInputMsgMultiSend = "op_weight_single_input_msg_multisend" + OpWeightMsgSetWithdrawAddress = "op_weight_msg_set_withdraw_address" + OpWeightMsgWithdrawDelegationReward = "op_weight_msg_withdraw_delegation_reward" + OpWeightMsgWithdrawValidatorCommission = "op_weight_msg_withdraw_validator_commission" + OpWeightSubmitVotingSlashingTextProposal = "op_weight_submit_voting_slashing_text_proposal" + OpWeightSubmitVotingSlashingCommunitySpendProposal = "op_weight_submit_voting_slashing_community_spend_proposal" + OpWeightSubmitVotingSlashingParamChangeProposal = "op_weight_submit_voting_slashing_param_change_proposal" + OpWeightMsgDeposit = "op_weight_msg_deposit" + OpWeightMsgCreateValidator = "op_weight_msg_create_validator" + OpWeightMsgEditValidator = "op_weight_msg_edit_validator" + OpWeightMsgDelegate = "op_weight_msg_delegate" + OpWeightMsgUndelegate = "op_weight_msg_undelegate" + OpWeightMsgBeginRedelegate = "op_weight_msg_begin_redelegate" + OpWeightMsgUnjail = "op_weight_msg_unjail" +) diff --git a/sim_test.go b/sim_test.go index cf9bd3929906..8ee536e554df 100644 --- a/sim_test.go +++ b/sim_test.go @@ -20,6 +20,7 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/genaccounts" @@ -41,6 +42,7 @@ import ( var ( genesisFile string + paramsFile string seed int64 numBlocks int blockSize int @@ -53,7 +55,8 @@ var ( ) func init() { - flag.StringVar(&genesisFile, "SimulationGenesis", "", "custom simulation genesis file") + flag.StringVar(&genesisFile, "SimulationGenesis", "", "custom simulation genesis file; cannot be used with params file") + flag.StringVar(¶msFile, "SimulationParams", "", "custom simulation params file which overrides any random params; cannot be used with genesis") flag.Int64Var(&seed, "SimulationSeed", 42, "simulation random seed") flag.IntVar(&numBlocks, "SimulationNumBlocks", 500, "number of blocks") flag.IntVar(&blockSize, "SimulationBlockSize", 200, "operations per block") @@ -74,18 +77,23 @@ func getSimulateFromSeedInput(tb testing.TB, w io.Writer, app *SimApp) ( testAndRunTxs(app), invariants(app), numBlocks, blockSize, commit, lean, onOperation } -func appStateFromGenesisFileFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time, +func appStateFromGenesisFileFn( + r *rand.Rand, _ []simulation.Account, _ time.Time, ) (json.RawMessage, []simulation.Account, string) { var genesis tmtypes.GenesisDoc cdc := MakeCodec() + bytes, err := ioutil.ReadFile(genesisFile) if err != nil { panic(err) } + cdc.MustUnmarshalJSON(bytes, &genesis) + var appState GenesisState cdc.MustUnmarshalJSON(genesis.AppState, &appState) + accounts := genaccounts.GetGenesisStateFromAppState(cdc, appState) var newAccs []simulation.Account @@ -95,29 +103,168 @@ func appStateFromGenesisFileFn(r *rand.Rand, accs []simulation.Account, genesisT // and these keys are never actually used to sign by mock Tendermint. privkeySeed := make([]byte, 15) r.Read(privkeySeed) + privKey := secp256k1.GenPrivKeySecp256k1(privkeySeed) newAccs = append(newAccs, simulation.Account{privKey, privKey.PubKey(), acc.Address}) } + return genesis.AppState, newAccs, genesis.ChainID } // TODO refactor out random initialization code to the modules -func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time, +func appStateRandomizedFn( + r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time, appParams simulation.AppParams, ) (json.RawMessage, []simulation.Account, string) { - var genesisAccounts []genaccounts.GenesisAccount - genesisState := NewDefaultGenesisState() cdc := MakeCodec() + genesisState := NewDefaultGenesisState() + + var ( + amount int64 + numInitiallyBonded int64 + ) + + appParams.GetOrGenerate(cdc, StakePerAccount, &amount, r, + func(r *rand.Rand) { amount = int64(r.Intn(1e12)) }) + appParams.GetOrGenerate(cdc, InitiallyBondedValidators, &amount, r, + func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(250)) }) - amount := int64(r.Intn(1e12)) - numInitiallyBonded := int64(r.Intn(250)) numAccs := int64(len(accs)) if numInitiallyBonded > numAccs { numInitiallyBonded = numAccs } - fmt.Printf("Selected randomly generated parameters for simulated genesis:\n"+ - "\t{amount of stake per account: %v, initially bonded validators: %v}\n", - amount, numInitiallyBonded) + + fmt.Printf( + `Selected randomly generated parameters for simulated genesis: +{ + stake_per_account: "%v", + initially_bonded_validators: "%v" +} +`, amount, numInitiallyBonded, + ) + + genGenesisAccounts(cdc, r, accs, genesisTimestamp, amount, numInitiallyBonded, genesisState) + genAuthGenesisState(cdc, r, appParams, genesisState) + genBankGenesisState(cdc, r, appParams, genesisState) + genGovGenesisState(cdc, r, appParams, genesisState) + genMintGenesisState(cdc, r, appParams, genesisState) + genDistrGenesisState(cdc, r, appParams, genesisState) + stakingGen := genStakingGenesisState(cdc, r, accs, amount, numAccs, numInitiallyBonded, appParams, genesisState) + genSlashingGenesisState(cdc, r, stakingGen, appParams, genesisState) + + appState, err := MakeCodec().MarshalJSON(genesisState) + if err != nil { + panic(err) + } + + return appState, accs, "simulation" +} + +func appStateFn( + r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time, +) (appState json.RawMessage, simAccs []simulation.Account, chainID string) { + + cdc := MakeCodec() + + switch { + case paramsFile != "" && genesisFile != "": + panic("cannot provide both a genesis file and a params file") + + case genesisFile != "": + appState, simAccs, chainID = appStateFromGenesisFileFn(r, accs, genesisTimestamp) + + case paramsFile != "": + appParams := make(simulation.AppParams) + bz, err := ioutil.ReadFile(paramsFile) + if err != nil { + panic(err) + } + + cdc.MustUnmarshalJSON(bz, &appParams) + appState, simAccs, chainID = appStateRandomizedFn(r, accs, genesisTimestamp, appParams) + + default: + appParams := make(simulation.AppParams) + appState, simAccs, chainID = appStateRandomizedFn(r, accs, genesisTimestamp, appParams) + } + + return appState, simAccs, chainID +} + +func genAuthGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams, genesisState map[string]json.RawMessage) { + authGenesis := auth.NewGenesisState( + nil, + auth.NewParams( + func(r *rand.Rand) uint64 { + var v uint64 + ap.GetOrGenerate(cdc, simulation.MaxMemoChars, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.MaxMemoChars](r).(uint64) + }) + return v + }(r), + func(r *rand.Rand) uint64 { + var v uint64 + ap.GetOrGenerate(cdc, simulation.TxSigLimit, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.TxSigLimit](r).(uint64) + }) + return v + }(r), + func(r *rand.Rand) uint64 { + var v uint64 + ap.GetOrGenerate(cdc, simulation.TxSizeCostPerByte, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.TxSizeCostPerByte](r).(uint64) + }) + return v + }(r), + func(r *rand.Rand) uint64 { + var v uint64 + ap.GetOrGenerate(cdc, simulation.SigVerifyCostED25519, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.SigVerifyCostED25519](r).(uint64) + }) + return v + }(r), + func(r *rand.Rand) uint64 { + var v uint64 + ap.GetOrGenerate(cdc, simulation.SigVerifyCostSECP256K1, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.SigVerifyCostSECP256K1](r).(uint64) + }) + return v + }(r), + ), + ) + + fmt.Printf("Selected randomly generated auth parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, authGenesis.Params)) + genesisState[auth.ModuleName] = cdc.MustMarshalJSON(authGenesis) +} + +func genBankGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams, genesisState map[string]json.RawMessage) { + bankGenesis := bank.NewGenesisState( + func(r *rand.Rand) bool { + var v bool + ap.GetOrGenerate(cdc, simulation.SendEnabled, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.SendEnabled](r).(bool) + }) + return v + }(r), + ) + + fmt.Printf("Selected randomly generated bank parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, bankGenesis)) + genesisState[bank.ModuleName] = cdc.MustMarshalJSON(bankGenesis) +} + +func genGenesisAccounts( + cdc *codec.Codec, r *rand.Rand, accs []simulation.Account, + genesisTimestamp time.Time, amount, numInitiallyBonded int64, + genesisState map[string]json.RawMessage, +) { + + var genesisAccounts []genaccounts.GenesisAccount // randomly generate some genesis accounts for i, acc := range accs { @@ -137,8 +284,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest startTime := genesisTimestamp.Unix() - // Allow for some vesting accounts to vest very quickly while others very - // slowly. + // Allow for some vesting accounts to vest very quickly while others very slowly. if r.Intn(100) < 50 { endTime = int64(simulation.RandIntBetween(r, int(startTime), int(startTime+(60*60*24*30)))) } else { @@ -168,84 +314,239 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest } genesisState[genaccounts.ModuleName] = cdc.MustMarshalJSON(genesisAccounts) +} - authGenesis := auth.NewGenesisState( - nil, - auth.NewParams( - simulation.ModuleParamSimulator["MaxMemoCharacters"](r).(uint64), - simulation.ModuleParamSimulator["TxSigLimit"](r).(uint64), - simulation.ModuleParamSimulator["TxSizeCostPerByte"](r).(uint64), - simulation.ModuleParamSimulator["SigVerifyCostED25519"](r).(uint64), - simulation.ModuleParamSimulator["SigVerifyCostSecp256k1"](r).(uint64), - ), - ) - fmt.Printf("Selected randomly generated auth parameters:\n\t%+v\n", authGenesis) - genesisState[auth.ModuleName] = cdc.MustMarshalJSON(authGenesis) - - bankGenesis := bank.NewGenesisState(r.Int63n(2) == 0) - genesisState[bank.ModuleName] = cdc.MustMarshalJSON(bankGenesis) - fmt.Printf("Selected randomly generated bank parameters:\n\t%+v\n", bankGenesis) +func genGovGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams, genesisState map[string]json.RawMessage) { + var vp time.Duration + ap.GetOrGenerate(cdc, simulation.VotingParamsVotingPeriod, &vp, r, + func(r *rand.Rand) { + vp = simulation.ModuleParamSimulator[simulation.VotingParamsVotingPeriod](r).(time.Duration) + }) - // Random genesis states - vp := simulation.ModuleParamSimulator["VotingParams/VotingPeriod"](r).(time.Duration) govGenesis := gov.NewGenesisState( uint64(r.Intn(100)), gov.NewDepositParams( - simulation.ModuleParamSimulator["DepositParams/MinDeposit"](r).(sdk.Coins), + func(r *rand.Rand) sdk.Coins { + var v sdk.Coins + ap.GetOrGenerate(cdc, simulation.DepositParamsMinDeposit, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.DepositParamsMinDeposit](r).(sdk.Coins) + }) + return v + }(r), vp, ), gov.NewVotingParams(vp), gov.NewTallyParams( - simulation.ModuleParamSimulator["TallyParams/Quorum"](r).(sdk.Dec), - simulation.ModuleParamSimulator["TallyParams/Threshold"](r).(sdk.Dec), - simulation.ModuleParamSimulator["TallyParams/Veto"](r).(sdk.Dec), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.TallyParamsQuorum, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.TallyParamsQuorum](r).(sdk.Dec) + }) + return v + }(r), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.TallyParamsThreshold, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.TallyParamsThreshold](r).(sdk.Dec) + }) + return v + }(r), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.TallyParamsVeto, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.TallyParamsVeto](r).(sdk.Dec) + }) + return v + }(r), ), ) + + fmt.Printf("Selected randomly generated governance parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, govGenesis)) genesisState[gov.ModuleName] = cdc.MustMarshalJSON(govGenesis) - fmt.Printf("Selected randomly generated governance parameters:\n\t%+v\n", govGenesis) +} - stakingGenesis := staking.NewGenesisState( - staking.InitialPool(), - staking.NewParams( - simulation.ModuleParamSimulator["UnbondingTime"](r).(time.Duration), - simulation.ModuleParamSimulator["MaxValidators"](r).(uint16), - 7, +func genMintGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams, genesisState map[string]json.RawMessage) { + mintGenesis := mint.NewGenesisState( + mint.InitialMinter( + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.Inflation, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.Inflation](r).(sdk.Dec) + }) + return v + }(r), + ), + mint.NewParams( sdk.DefaultBondDenom, + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.InflationRateChange, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.InflationRateChange](r).(sdk.Dec) + }) + return v + }(r), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.InflationMax, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.InflationMax](r).(sdk.Dec) + }) + return v + }(r), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.InflationMin, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.InflationMin](r).(sdk.Dec) + }) + return v + }(r), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.GoalBonded, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.GoalBonded](r).(sdk.Dec) + }) + return v + }(r), + uint64(60*60*8766/5), + ), + ) + + fmt.Printf("Selected randomly generated minting parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, mintGenesis.Params)) + genesisState[mint.ModuleName] = cdc.MustMarshalJSON(mintGenesis) +} + +func genDistrGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams, genesisState map[string]json.RawMessage) { + distrGenesis := distr.GenesisState{ + FeePool: distr.InitialFeePool(), + CommunityTax: func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.CommunityTax, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.CommunityTax](r).(sdk.Dec) + }) + return v + }(r), + BaseProposerReward: func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.BaseProposerReward, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.BaseProposerReward](r).(sdk.Dec) + }) + return v + }(r), + BonusProposerReward: func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.BonusProposerReward, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.BonusProposerReward](r).(sdk.Dec) + }) + return v + }(r), + } + + fmt.Printf("Selected randomly generated distribution parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, distrGenesis)) + genesisState[distr.ModuleName] = cdc.MustMarshalJSON(distrGenesis) +} + +func genSlashingGenesisState( + cdc *codec.Codec, r *rand.Rand, stakingGen staking.GenesisState, + ap simulation.AppParams, genesisState map[string]json.RawMessage, +) { + slashingGenesis := slashing.NewGenesisState( + slashing.NewParams( + stakingGen.Params.UnbondingTime, + func(r *rand.Rand) int64 { + var v int64 + ap.GetOrGenerate(cdc, simulation.SignedBlocksWindow, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.SignedBlocksWindow](r).(int64) + }) + return v + }(r), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.MinSignedPerWindow, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.MinSignedPerWindow](r).(sdk.Dec) + }) + return v + }(r), + func(r *rand.Rand) time.Duration { + var v time.Duration + ap.GetOrGenerate(cdc, simulation.DowntimeJailDuration, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.DowntimeJailDuration](r).(time.Duration) + }) + return v + }(r), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.SlashFractionDoubleSign, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.SlashFractionDoubleSign](r).(sdk.Dec) + }) + return v + }(r), + func(r *rand.Rand) sdk.Dec { + var v sdk.Dec + ap.GetOrGenerate(cdc, simulation.SlashFractionDowntime, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.SlashFractionDowntime](r).(sdk.Dec) + }) + return v + }(r), ), nil, nil, ) - fmt.Printf("Selected randomly generated staking parameters:\n\t%+v\n", stakingGenesis) - - slashingParams := slashing.NewParams( - stakingGenesis.Params.UnbondingTime, - simulation.ModuleParamSimulator["SignedBlocksWindow"](r).(int64), - simulation.ModuleParamSimulator["MinSignedPerWindow"](r).(sdk.Dec), - simulation.ModuleParamSimulator["DowntimeJailDuration"](r).(time.Duration), - simulation.ModuleParamSimulator["SlashFractionDoubleSign"](r).(sdk.Dec), - simulation.ModuleParamSimulator["SlashFractionDowntime"](r).(sdk.Dec), - ) - slashingGenesis := slashing.NewGenesisState(slashingParams, nil, nil) + + fmt.Printf("Selected randomly generated slashing parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, slashingGenesis.Params)) genesisState[slashing.ModuleName] = cdc.MustMarshalJSON(slashingGenesis) - fmt.Printf("Selected randomly generated slashing parameters:\n\t%+v\n", slashingGenesis) +} - mintGenesis := mint.NewGenesisState( - mint.InitialMinter( - sdk.NewDecWithPrec(int64(r.Intn(99)), 2)), - mint.NewParams( +func genStakingGenesisState( + cdc *codec.Codec, r *rand.Rand, accs []simulation.Account, amount, numAccs, numInitiallyBonded int64, + ap simulation.AppParams, genesisState map[string]json.RawMessage, +) staking.GenesisState { + + stakingGenesis := staking.NewGenesisState( + staking.InitialPool(), + staking.NewParams( + func(r *rand.Rand) time.Duration { + var v time.Duration + ap.GetOrGenerate(cdc, simulation.UnbondingTime, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.UnbondingTime](r).(time.Duration) + }) + return v + }(r), + func(r *rand.Rand) uint16 { + var v uint16 + ap.GetOrGenerate(cdc, simulation.MaxValidators, &v, r, + func(r *rand.Rand) { + v = simulation.ModuleParamSimulator[simulation.MaxValidators](r).(uint16) + }) + return v + }(r), + 7, sdk.DefaultBondDenom, - simulation.ModuleParamSimulator["InflationRateChange"](r).(sdk.Dec), - simulation.ModuleParamSimulator["InflationMax"](r).(sdk.Dec), - simulation.ModuleParamSimulator["InflationMin"](r).(sdk.Dec), - simulation.ModuleParamSimulator["GoalBonded"](r).(sdk.Dec), - uint64(60*60*8766/5), ), + nil, + nil, ) - genesisState[mint.ModuleName] = cdc.MustMarshalJSON(mintGenesis) - fmt.Printf("Selected randomly generated minting parameters:\n\t%+v\n", mintGenesis) - var validators []staking.Validator - var delegations []staking.Delegation + var ( + validators []staking.Validator + delegations []staking.Delegation + ) valAddrs := make([]sdk.ValAddress, numInitiallyBonded) for i := 0; i < int(numInitiallyBonded); i++ { @@ -255,7 +556,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest validator := staking.NewValidator(valAddr, accs[i].PubKey, staking.Description{}) validator.Tokens = sdk.NewInt(amount) validator.DelegatorShares = sdk.NewDec(amount) - delegation := staking.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount)} + delegation := staking.NewDelegation(accs[i].Address, valAddr, sdk.NewDec(amount)) validators = append(validators, validator) delegations = append(delegations, delegation) } @@ -263,54 +564,203 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest stakingGenesis.Pool.NotBondedTokens = sdk.NewInt((amount * numAccs) + (numInitiallyBonded * amount)) stakingGenesis.Validators = validators stakingGenesis.Delegations = delegations - genesisState[staking.ModuleName] = cdc.MustMarshalJSON(stakingGenesis) - - // TODO make use NewGenesisState - distrGenesis := distr.GenesisState{ - FeePool: distr.InitialFeePool(), - CommunityTax: sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)), - BaseProposerReward: sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)), - BonusProposerReward: sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)), - } - genesisState[distr.ModuleName] = cdc.MustMarshalJSON(distrGenesis) - fmt.Printf("Selected randomly generated distribution parameters:\n\t%+v\n", distrGenesis) - // Marshal genesis - appState, err := MakeCodec().MarshalJSON(genesisState) - if err != nil { - panic(err) - } + fmt.Printf("Selected randomly generated staking parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, stakingGenesis.Params)) + genesisState[staking.ModuleName] = cdc.MustMarshalJSON(stakingGenesis) - return appState, accs, "simulation" + return stakingGenesis } -func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time, -) (json.RawMessage, []simulation.Account, string) { +func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { + cdc := MakeCodec() + ap := make(simulation.AppParams) - if genesisFile != "" { - return appStateFromGenesisFileFn(r, accs, genesisTimestamp) + if paramsFile != "" { + bz, err := ioutil.ReadFile(paramsFile) + if err != nil { + panic(err) + } + + cdc.MustUnmarshalJSON(bz, &ap) } - return appStateRandomizedFn(r, accs, genesisTimestamp) -} -func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { return []simulation.WeightedOperation{ - {5, authsim.SimulateDeductFee(app.accountKeeper, app.feeCollectionKeeper)}, - {100, banksim.SimulateMsgSend(app.accountKeeper, app.bankKeeper)}, - {10, banksim.SimulateSingleInputMsgMultiSend(app.accountKeeper, app.bankKeeper)}, - {50, distrsim.SimulateMsgSetWithdrawAddress(app.accountKeeper, app.distrKeeper)}, - {50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)}, - {50, distrsim.SimulateMsgWithdrawValidatorCommission(app.accountKeeper, app.distrKeeper)}, - {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, govsim.SimulateTextProposalContent)}, - {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, distrsim.SimulateCommunityPoolSpendProposalContent(app.distrKeeper))}, - {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, paramsim.SimulateParamChangeProposalContent)}, - {100, govsim.SimulateMsgDeposit(app.govKeeper)}, - {100, stakingsim.SimulateMsgCreateValidator(app.accountKeeper, app.stakingKeeper)}, - {5, stakingsim.SimulateMsgEditValidator(app.stakingKeeper)}, - {100, stakingsim.SimulateMsgDelegate(app.accountKeeper, app.stakingKeeper)}, - {100, stakingsim.SimulateMsgUndelegate(app.accountKeeper, app.stakingKeeper)}, - {100, stakingsim.SimulateMsgBeginRedelegate(app.accountKeeper, app.stakingKeeper)}, - {100, slashingsim.SimulateMsgUnjail(app.slashingKeeper)}, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightDeductFee, &v, nil, + func(_ *rand.Rand) { + v = 5 + }) + return v + }(nil), + authsim.SimulateDeductFee(app.accountKeeper, app.feeCollectionKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgSend, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + banksim.SimulateMsgSend(app.accountKeeper, app.bankKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightSingleInputMsgMultiSend, &v, nil, + func(_ *rand.Rand) { + v = 10 + }) + return v + }(nil), + banksim.SimulateSingleInputMsgMultiSend(app.accountKeeper, app.bankKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgSetWithdrawAddress, &v, nil, + func(_ *rand.Rand) { + v = 50 + }) + return v + }(nil), + distrsim.SimulateMsgSetWithdrawAddress(app.accountKeeper, app.distrKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgWithdrawDelegationReward, &v, nil, + func(_ *rand.Rand) { + v = 50 + }) + return v + }(nil), + distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgWithdrawValidatorCommission, &v, nil, + func(_ *rand.Rand) { + v = 50 + }) + return v + }(nil), + distrsim.SimulateMsgWithdrawValidatorCommission(app.accountKeeper, app.distrKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightSubmitVotingSlashingTextProposal, &v, nil, + func(_ *rand.Rand) { + v = 5 + }) + return v + }(nil), + govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, govsim.SimulateTextProposalContent), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightSubmitVotingSlashingCommunitySpendProposal, &v, nil, + func(_ *rand.Rand) { + v = 5 + }) + return v + }(nil), + govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, distrsim.SimulateCommunityPoolSpendProposalContent(app.distrKeeper)), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightSubmitVotingSlashingParamChangeProposal, &v, nil, + func(_ *rand.Rand) { + v = 5 + }) + return v + }(nil), + govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, paramsim.SimulateParamChangeProposalContent), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgDeposit, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + govsim.SimulateMsgDeposit(app.govKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgCreateValidator, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + stakingsim.SimulateMsgCreateValidator(app.accountKeeper, app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgEditValidator, &v, nil, + func(_ *rand.Rand) { + v = 5 + }) + return v + }(nil), + stakingsim.SimulateMsgEditValidator(app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgDelegate, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + stakingsim.SimulateMsgDelegate(app.accountKeeper, app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgUndelegate, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + stakingsim.SimulateMsgUndelegate(app.accountKeeper, app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgBeginRedelegate, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + stakingsim.SimulateMsgBeginRedelegate(app.accountKeeper, app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgUnjail, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + slashingsim.SimulateMsgUnjail(app.slashingKeeper), + }, } } diff --git a/test_util.go b/test_util.go index 8b79e757a9b5..ea5f7b05485c 100644 --- a/test_util.go +++ b/test_util.go @@ -3,12 +3,12 @@ package simapp import ( "io" + dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" - dbm "github.com/tendermint/tendermint/libs/db" ) // NewSimAppUNSAFE is used for debugging purposes only.