From 6606007297442e02662d6133e29cb9f29f987c7e Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Sat, 8 Jun 2019 16:55:47 -0400 Subject: [PATCH] Merge PR #4435: Simulation: Params from File * Move distr params to simulation param generator * Cleanup sim params output * Print params when possible instead of entire genesis struct * Update module param simulator keys * Rename module params simulator keys * Add/fix missing module params and implement SimParams * UPdate sim test to accept params file * Allow weights to be provided from file * Create doc.go * Remove TODO * Implement MustMarshalJSONIndent * Use mustMarshalJSONIndent for non-codec * Remove TODO * Fix doc.go * Update points * Update simapp/doc.go Co-Authored-By: Alessio Treglia * Update simapp/doc.go Co-Authored-By: Alessio Treglia * Cleanup appStateFn * Use constants for simulation parameter keys * Update msgs.go * minor formatting --- codec/codec.go | 11 + simapp/doc.go | 93 +++++ simapp/params.go | 23 ++ simapp/sim_test.go | 668 ++++++++++++++++++++++++++++++------ simapp/test_util.go | 2 +- x/params/simulation/msgs.go | 28 +- x/simulation/params.go | 116 +++++-- x/simulation/simulate.go | 10 +- x/simulation/util.go | 26 +- 9 files changed, 800 insertions(+), 177 deletions(-) create mode 100644 simapp/doc.go create mode 100644 simapp/params.go diff --git a/codec/codec.go b/codec/codec.go index 27b97d9c6..5e8f94d19 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -3,6 +3,7 @@ package codec import ( "bytes" "encoding/json" + "fmt" amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" @@ -35,6 +36,16 @@ func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) { return out.Bytes(), nil } +// MustMarshalJSONIndent executes MarshalJSONIndent except it panics upon failure. +func MustMarshalJSONIndent(cdc *Codec, obj interface{}) []byte { + bz, err := MarshalJSONIndent(cdc, obj) + if err != nil { + panic(fmt.Sprintf("failed to marshal JSON: %s", err)) + } + + return bz +} + //__________________________________________________________________ // generic sealed codec to be used throughout sdk diff --git a/simapp/doc.go b/simapp/doc.go new file mode 100644 index 000000000..ee76bca1e --- /dev/null +++ b/simapp/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/simapp/params.go b/simapp/params.go new file mode 100644 index 000000000..b081715f3 --- /dev/null +++ b/simapp/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/simapp/sim_test.go b/simapp/sim_test.go index cf9bd3929..8ee536e55 100644 --- a/simapp/sim_test.go +++ b/simapp/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) +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) + }) - 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) - - // 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) +} + +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 slashing parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, slashingGenesis.Params)) + genesisState[slashing.ModuleName] = cdc.MustMarshalJSON(slashingGenesis) +} + +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( - simulation.ModuleParamSimulator["UnbondingTime"](r).(time.Duration), - simulation.ModuleParamSimulator["MaxValidators"](r).(uint16), + 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, ), 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), + var ( + validators []staking.Validator + delegations []staking.Delegation ) - slashingGenesis := slashing.NewGenesisState(slashingParams, nil, nil) - 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( - 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), - ), - ) - 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 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 + + fmt.Printf("Selected randomly generated staking parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, stakingGenesis.Params)) 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) - } - - return appState, accs, "simulation" -} - -func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time, -) (json.RawMessage, []simulation.Account, string) { - - if genesisFile != "" { - return appStateFromGenesisFileFn(r, accs, genesisTimestamp) - } - return appStateRandomizedFn(r, accs, genesisTimestamp) + return stakingGenesis } func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { + cdc := MakeCodec() + ap := make(simulation.AppParams) + + if paramsFile != "" { + bz, err := ioutil.ReadFile(paramsFile) + if err != nil { + panic(err) + } + + cdc.MustUnmarshalJSON(bz, &ap) + } + 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/simapp/test_util.go b/simapp/test_util.go index 8b79e757a..ea5f7b054 100644 --- a/simapp/test_util.go +++ b/simapp/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. diff --git a/x/params/simulation/msgs.go b/x/params/simulation/msgs.go index c77e3db80..df303a8b2 100644 --- a/x/params/simulation/msgs.go +++ b/x/params/simulation/msgs.go @@ -34,7 +34,7 @@ var paramChangePool = []simParamChange{ "MaxValidators", "", func(r *rand.Rand) string { - return fmt.Sprintf("%d", simulation.ModuleParamSimulator["MaxValidators"](r).(uint16)) + return fmt.Sprintf("%d", simulation.ModuleParamSimulator[simulation.MaxValidators](r).(uint16)) }, }, { @@ -42,7 +42,7 @@ var paramChangePool = []simParamChange{ "UnbondingTime", "", func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator["UnbondingTime"](r).(time.Duration)) + return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.UnbondingTime](r).(time.Duration)) }, }, // slashing parameters @@ -51,7 +51,7 @@ var paramChangePool = []simParamChange{ "SignedBlocksWindow", "", func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator["SignedBlocksWindow"](r).(int64)) + return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.SignedBlocksWindow](r).(int64)) }, }, { @@ -59,7 +59,7 @@ var paramChangePool = []simParamChange{ "MinSignedPerWindow", "", func(r *rand.Rand) string { - return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator["MinSignedPerWindow"](r).(sdk.Dec)) + return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator[simulation.MinSignedPerWindow](r).(sdk.Dec)) }, }, { @@ -67,7 +67,7 @@ var paramChangePool = []simParamChange{ "SlashFractionDowntime", "", func(r *rand.Rand) string { - return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator["SlashFractionDowntime"](r).(sdk.Dec)) + return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator[simulation.SlashFractionDowntime](r).(sdk.Dec)) }, }, // minting parameters @@ -76,7 +76,7 @@ var paramChangePool = []simParamChange{ "InflationRateChange", "", func(r *rand.Rand) string { - return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator["InflationRateChange"](r).(sdk.Dec)) + return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator[simulation.InflationRateChange](r).(sdk.Dec)) }, }, // gov parameters @@ -85,7 +85,7 @@ var paramChangePool = []simParamChange{ "votingparams", "", func(r *rand.Rand) string { - return fmt.Sprintf(`{"voting_period": "%d"}`, simulation.ModuleParamSimulator["VotingParams/VotingPeriod"](r).(time.Duration)) + return fmt.Sprintf(`{"voting_period": "%d"}`, simulation.ModuleParamSimulator[simulation.VotingParamsVotingPeriod](r).(time.Duration)) }, }, { @@ -93,7 +93,7 @@ var paramChangePool = []simParamChange{ "depositparams", "", func(r *rand.Rand) string { - return fmt.Sprintf(`{"max_deposit_period": "%d"}`, simulation.ModuleParamSimulator["VotingParams/VotingPeriod"](r).(time.Duration)) + return fmt.Sprintf(`{"max_deposit_period": "%d"}`, simulation.ModuleParamSimulator[simulation.VotingParamsVotingPeriod](r).(time.Duration)) }, }, { @@ -105,9 +105,9 @@ var paramChangePool = []simParamChange{ key string value sdk.Dec }{ - {"quorum", simulation.ModuleParamSimulator["TallyParams/Quorum"](r).(sdk.Dec)}, - {"threshold", simulation.ModuleParamSimulator["TallyParams/Threshold"](r).(sdk.Dec)}, - {"veto", simulation.ModuleParamSimulator["TallyParams/Veto"](r).(sdk.Dec)}, + {"quorum", simulation.ModuleParamSimulator[simulation.TallyParamsQuorum](r).(sdk.Dec)}, + {"threshold", simulation.ModuleParamSimulator[simulation.TallyParamsThreshold](r).(sdk.Dec)}, + {"veto", simulation.ModuleParamSimulator[simulation.TallyParamsVeto](r).(sdk.Dec)}, } pc := make(map[string]string) @@ -134,7 +134,7 @@ var paramChangePool = []simParamChange{ "MaxMemoCharacters", "", func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator["MaxMemoCharacters"](r).(uint64)) + return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.MaxMemoChars](r).(uint64)) }, }, { @@ -142,7 +142,7 @@ var paramChangePool = []simParamChange{ "TxSigLimit", "", func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator["TxSigLimit"](r).(uint64)) + return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.TxSigLimit](r).(uint64)) }, }, { @@ -150,7 +150,7 @@ var paramChangePool = []simParamChange{ "TxSizeCostPerByte", "", func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator["TxSizeCostPerByte"](r).(uint64)) + return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.TxSizeCostPerByte](r).(uint64)) }, }, } diff --git a/x/simulation/params.go b/x/simulation/params.go index 0f689cecb..643360461 100644 --- a/x/simulation/params.go +++ b/x/simulation/params.go @@ -1,9 +1,11 @@ package simulation import ( + "encoding/json" "math/rand" "time" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -13,6 +15,34 @@ const ( // Maximum time per block maxTimePerBlock int64 = 10000 + + // Simulation parameter constants + SendEnabled = "send_enabled" + MaxMemoChars = "max_memo_characters" + TxSigLimit = "tx_sig_limit" + TxSizeCostPerByte = "tx_size_cost_per_byte" + SigVerifyCostED25519 = "sig_verify_cost_ed25519" + SigVerifyCostSECP256K1 = "sig_verify_cost_secp256k1" + DepositParamsMinDeposit = "deposit_params_min_deposit" + VotingParamsVotingPeriod = "voting_params_voting_period" + TallyParamsQuorum = "tally_params_quorum" + TallyParamsThreshold = "tally_params_threshold" + TallyParamsVeto = "tally_params_veto" + UnbondingTime = "unbonding_time" + MaxValidators = "max_validators" + SignedBlocksWindow = "signed_blocks_window" + MinSignedPerWindow = "min_signed_per_window" + DowntimeJailDuration = "downtime_jail_duration" + SlashFractionDoubleSign = "slash_fraction_double_sign" + SlashFractionDowntime = "slash_fraction_downtime" + InflationRateChange = "inflation_rate_change" + Inflation = "inflation" + InflationMax = "inflation_max" + InflationMin = "inflation_min" + GoalBonded = "goal_bonded" + CommunityTax = "community_tax" + BaseProposerReward = "base_proposer_reward" + BonusProposerReward = "bonus_proposer_reward" ) // TODO explain transitional matrix usage @@ -37,72 +67,104 @@ var ( // values simulated should be within valid acceptable range for the given // parameter. ModuleParamSimulator = map[string]func(r *rand.Rand) interface{}{ - "MaxMemoCharacters": func(r *rand.Rand) interface{} { + SendEnabled: func(r *rand.Rand) interface{} { + return r.Int63n(2) == 0 + }, + MaxMemoChars: func(r *rand.Rand) interface{} { return uint64(RandIntBetween(r, 100, 200)) }, - "TxSigLimit": func(r *rand.Rand) interface{} { + TxSigLimit: func(r *rand.Rand) interface{} { return uint64(r.Intn(7) + 1) }, - "TxSizeCostPerByte": func(r *rand.Rand) interface{} { + TxSizeCostPerByte: func(r *rand.Rand) interface{} { return uint64(RandIntBetween(r, 5, 15)) }, - "SigVerifyCostED25519": func(r *rand.Rand) interface{} { + SigVerifyCostED25519: func(r *rand.Rand) interface{} { return uint64(RandIntBetween(r, 500, 1000)) }, - "SigVerifyCostSecp256k1": func(r *rand.Rand) interface{} { + SigVerifyCostSECP256K1: func(r *rand.Rand) interface{} { return uint64(RandIntBetween(r, 500, 1000)) }, - "DepositParams/MinDeposit": func(r *rand.Rand) interface{} { + DepositParamsMinDeposit: func(r *rand.Rand) interface{} { return sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, int64(RandIntBetween(r, 1, 1e3)))} }, - "VotingParams/VotingPeriod": func(r *rand.Rand) interface{} { + VotingParamsVotingPeriod: func(r *rand.Rand) interface{} { return time.Duration(RandIntBetween(r, 1, 2*60*60*24*2)) * time.Second }, - "TallyParams/Quorum": func(r *rand.Rand) interface{} { + TallyParamsQuorum: func(r *rand.Rand) interface{} { return sdk.NewDecWithPrec(int64(RandIntBetween(r, 334, 500)), 3) }, - "TallyParams/Threshold": func(r *rand.Rand) interface{} { + TallyParamsThreshold: func(r *rand.Rand) interface{} { return sdk.NewDecWithPrec(int64(RandIntBetween(r, 450, 550)), 3) }, - "TallyParams/Veto": func(r *rand.Rand) interface{} { + TallyParamsVeto: func(r *rand.Rand) interface{} { return sdk.NewDecWithPrec(int64(RandIntBetween(r, 250, 334)), 3) }, - "UnbondingTime": func(r *rand.Rand) interface{} { + UnbondingTime: func(r *rand.Rand) interface{} { return time.Duration(RandIntBetween(r, 60, 60*60*24*3*2)) * time.Second }, - "MaxValidators": func(r *rand.Rand) interface{} { + MaxValidators: func(r *rand.Rand) interface{} { return uint16(r.Intn(250) + 1) }, - "SignedBlocksWindow": func(r *rand.Rand) interface{} { + SignedBlocksWindow: func(r *rand.Rand) interface{} { return int64(RandIntBetween(r, 10, 1000)) }, - "MinSignedPerWindow": func(r *rand.Rand) interface{} { + MinSignedPerWindow: func(r *rand.Rand) interface{} { return sdk.NewDecWithPrec(int64(r.Intn(10)), 1) }, - "DowntimeJailDuration": func(r *rand.Rand) interface{} { + DowntimeJailDuration: func(r *rand.Rand) interface{} { return time.Duration(RandIntBetween(r, 60, 60*60*24)) * time.Second }, - "SlashFractionDoubleSign": func(r *rand.Rand) interface{} { + SlashFractionDoubleSign: func(r *rand.Rand) interface{} { return sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))) }, - "SlashFractionDowntime": func(r *rand.Rand) interface{} { + SlashFractionDowntime: func(r *rand.Rand) interface{} { return sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))) }, - "InflationRateChange": func(r *rand.Rand) interface{} { + InflationRateChange: func(r *rand.Rand) interface{} { return sdk.NewDecWithPrec(int64(r.Intn(99)), 2) }, - "InflationMax": func(r *rand.Rand) interface{} { + Inflation: func(r *rand.Rand) interface{} { + return sdk.NewDecWithPrec(int64(r.Intn(99)), 2) + }, + InflationMax: func(r *rand.Rand) interface{} { return sdk.NewDecWithPrec(20, 2) }, - "InflationMin": func(r *rand.Rand) interface{} { + InflationMin: func(r *rand.Rand) interface{} { return sdk.NewDecWithPrec(7, 2) }, - "GoalBonded": func(r *rand.Rand) interface{} { + GoalBonded: func(r *rand.Rand) interface{} { return sdk.NewDecWithPrec(67, 2) }, + CommunityTax: func(r *rand.Rand) interface{} { + return sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)) + }, + BaseProposerReward: func(r *rand.Rand) interface{} { + return sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)) + }, + BonusProposerReward: func(r *rand.Rand) interface{} { + return sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)) + }, } ) +type ( + AppParams map[string]json.RawMessage + ParamSimulator func(r *rand.Rand) +) + +// GetOrGenerate attempts to get a given parameter by key from the AppParams +// object. If it exists, it'll be decoded and returned. Otherwise, the provided +// ParamSimulator is used to generate a random value. +func (sp AppParams) GetOrGenerate(cdc *codec.Codec, key string, ptr interface{}, r *rand.Rand, ps ParamSimulator) { + if v, ok := sp[key]; ok && v != nil { + cdc.MustUnmarshalJSON(v, ptr) + return + } + + ps(r) +} + // Simulation parameters type Params struct { PastEvidenceFraction float64 @@ -113,18 +175,6 @@ type Params struct { BlockSizeTransitionMatrix TransitionMatrix } -// Return default simulation parameters -func DefaultParams() Params { - return Params{ - PastEvidenceFraction: 0.5, - NumKeys: 250, - EvidenceFraction: 0.5, - InitialLivenessWeightings: []int{40, 5, 5}, - LivenessTransitionMatrix: defaultLivenessTransitionMatrix, - BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix, - } -} - // Return random simulation parameters func RandomParams(r *rand.Rand) Params { return Params{ diff --git a/x/simulation/simulate.go b/x/simulation/simulate.go index 4fa5004d0..b6f753933 100644 --- a/x/simulation/simulate.go +++ b/x/simulation/simulate.go @@ -54,12 +54,14 @@ func SimulateFromSeed( fmt.Fprintf(w, "Starting SimulateFromSeed with randomness created with seed %d\n", int(seed)) r := rand.New(rand.NewSource(seed)) - params := RandomParams(r) // := DefaultParams() - fmt.Fprintf(w, "Randomized simulation params: %+v\n", params) + params := RandomParams(r) + fmt.Fprintf(w, "Randomized simulation params: \n%s\n", mustMarshalJSONIndent(params)) genesisTimestamp := RandTimestamp(r) - fmt.Printf("Starting the simulation from time %v, unixtime %v\n", - genesisTimestamp.UTC().Format(time.UnixDate), genesisTimestamp.Unix()) + fmt.Printf( + "Starting the simulation from time %v, unixtime %v\n", + genesisTimestamp.UTC().Format(time.UnixDate), genesisTimestamp.Unix(), + ) timeDiff := maxTimePerBlock - minTimePerBlock accs := RandomAccounts(r, params.NumKeys) diff --git a/x/simulation/util.go b/x/simulation/util.go index 3badf25a9..49f6bc544 100644 --- a/x/simulation/util.go +++ b/x/simulation/util.go @@ -1,6 +1,7 @@ package simulation import ( + "encoding/json" "fmt" "math/rand" "testing" @@ -60,22 +61,6 @@ func getBlockSize(r *rand.Rand, params Params, return state, blocksize } -// PeriodicInvariant returns an Invariant function closure that asserts a given -// invariant if the mock application's last block modulo the given period is -// congruent to the given offset. -// -// NOTE this function is intended to be used manually used while running -// computationally heavy simulations. -// TODO reference this function in the codebase probably through use of a switch -func PeriodicInvariant(invariant sdk.Invariant, period int, offset int) sdk.Invariant { - return func(ctx sdk.Context) error { - if int(ctx.BlockHeight())%period == offset { - return invariant(ctx) - } - return nil - } -} - // PeriodicInvariants returns an array of wrapped Invariants. Where each // invariant function is only executed periodically defined by period and offset. func PeriodicInvariants(invariants []sdk.Invariant, period int, offset int) []sdk.Invariant { @@ -91,3 +76,12 @@ func PeriodicInvariants(invariants []sdk.Invariant, period int, offset int) []sd } return outInvariants } + +func mustMarshalJSONIndent(o interface{}) []byte { + bz, err := json.MarshalIndent(o, "", " ") + if err != nil { + panic(fmt.Sprintf("failed to JSON encode: %s", err)) + } + + return bz +}