diff --git a/.circleci/config.yml b/.circleci/config.yml index 43b8dcfa7924..36dc04302660 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,9 +26,9 @@ commands: - go-src-v1-{{ .Revision }} - checkout - restore_cache: - name: "Restore go modules cache" - keys: - - go-mod-v2-{{ checksum "go.sum" }} + name: "Restore go modules cache" + keys: + - go-mod-v2-{{ checksum "go.sum" }} - run: name: << parameters.description >> command: | @@ -40,7 +40,7 @@ jobs: steps: - checkout - run: - Name : "Build docs" + name: "Build docs" command: make build-docs - run: name: "Upload docs to S3" @@ -87,13 +87,6 @@ jobs: - make: target: test_sim_import_export description: "Test application import/export simulation" - - run: - command: | - mkdir -p /tmp/errors - cp /tmp/**/app-simulation-seed* /tmp/errors/ - when: on_fail - - store_artifacts: - path: /tmp/errors test_sim_after_import: executor: golang @@ -101,13 +94,6 @@ jobs: - make: target: test_sim_after_import description: "Test simulation after import" - - run: - command: | - mkdir -p /tmp/errors - cp /tmp/**/app-simulation-seed* /tmp/errors/ - when: on_fail - - store_artifacts: - path: /tmp/errors test_sim_multi_seed_long: executor: golang @@ -115,13 +101,6 @@ jobs: - make: target: test_sim_multi_seed_long description: "Test multi-seed simulation (long)" - - run: - command: | - mkdir -p /tmp/errors - cp /tmp/**/app-simulation-seed* /tmp/errors/ - when: on_fail - - store_artifacts: - path: /tmp/errors test_sim_multi_seed_short: executor: golang @@ -129,14 +108,6 @@ jobs: - make: target: test_sim_multi_seed_short description: "Test multi-seed simulation (short)" - - run: - command: | - mkdir -p /tmp/errors - cp /tmp/**/app-simulation-seed* /tmp/errors/ - when: on_fail - - - store_artifacts: - path: /tmp/errors test_cover: executor: golang diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c90f97d09d9..177ff796948d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,9 +61,12 @@ longer panics if the store to load contains substores that we didn't explicitly * (simulation) [\#4824](https://github.com/cosmos/cosmos-sdk/issues/4824) PrintAllInvariants flag will print all failed invariants * (simulation) [\#4490](https://github.com/cosmos/cosmos-sdk/issues/4490) add `InitialBlockHeight` flag to resume a simulation from a given block * Support exporting the simulation stats to a given JSON file -* (simulation) [\#4847](https://github.com/cosmos/cosmos-sdk/issues/4847) `SimApp` and simulation refactors +* (simulation) [\#4847](https://github.com/cosmos/cosmos-sdk/issues/4847), [\#4838](https://github.com/cosmos/cosmos-sdk/pull/4838) `SimApp` and simulation refactors * Implement `SimulationManager` for executing modules' simulation functionalities in a modularized way - * Add `DecodeStore` to the `SimulationManager` for decoding each module's types + * Add `RegisterStoreDecoders` to the `SimulationManager` for decoding each module's types + * Add `GenerateGenesisStates` to the `SimulationManager` to generate a randomized `GenState` for each module + * Add `RandomizedParams` to the `SimulationManager` that registers each modules' parameters `Content` to simulate + `ParamChangeProposal`s * (simulation) [\#4893](https://github.com/cosmos/cosmos-sdk/issues/4893) Change SimApp keepers to be public and add getter functions for keys and codec * (simulation) [\#4906](https://github.com/cosmos/cosmos-sdk/issues/4906) Add simulation `Config` struct that wraps simulation flags * (store) [\#4792](https://github.com/cosmos/cosmos-sdk/issues/4792) panic on non-registered store diff --git a/Makefile b/Makefile index bcaf5014c8a1..7a846f986138 100644 --- a/Makefile +++ b/Makefile @@ -105,7 +105,7 @@ test_race: test_sim_nondeterminism: @echo "Running non-determinism test..." @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ - -NumBlocks=100 -BlockSize=200 -Commit=true -v -timeout 24h + -NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h test_sim_custom_genesis_fast: @echo "Running custom genesis simulation..." diff --git a/go.mod b/go.mod index a010df7e1e7b..90d905cfebde 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 github.com/cosmos/ledger-cosmos-go v0.10.3 - github.com/cosmos/tools v0.0.0-20190729191304-444fa9c55188 // indirect github.com/fortytw2/leaktest v1.3.0 // indirect github.com/gogo/protobuf v1.2.1 github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129 diff --git a/go.sum b/go.sum index b4ea88ed3d25..d4983bef4464 100644 --- a/go.sum +++ b/go.sum @@ -33,10 +33,6 @@ github.com/cosmos/ledger-cosmos-go v0.10.3 h1:Qhi5yTR5Pg1CaTpd00pxlGwNl4sFRdtK1J github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= -github.com/cosmos/tools v0.0.0-20190729191304-444fa9c55188 h1:KZsNQXLq7ZUURaBjVrztEqZX+d7qpjQkS4a0jbCMHIY= -github.com/cosmos/tools v0.0.0-20190729191304-444fa9c55188/go.mod h1:ycjJZ351OX/Y/DYgZqNn1WLCgpmVH7j29THN8vjbb9U= -github.com/cosmos/tools/cmd/clog v0.0.0-20190722180430-ea942c183cba h1:YhVnGzBkE2TvfBW5fAYBdNVK/3bwTPYVbMaOIGRHFRY= -github.com/cosmos/tools/cmd/clog v0.0.0-20190722180430-ea942c183cba/go.mod h1:TdPuAVaU2rc6K24ejr/AnGznt9Fd2qjtMoRrTO4uFrI= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/simapp/app.go b/simapp/app.go index 9eb594a9dd2d..5160f786177e 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -184,9 +184,9 @@ func NewSimApp( bank.NewAppModule(app.BankKeeper, app.AccountKeeper), crisis.NewAppModule(&app.CrisisKeeper), supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper), - distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), gov.NewAppModule(app.GovKeeper, app.SupplyKeeper), mint.NewAppModule(app.MintKeeper), + distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), nft.NewAppModule(app.NFTKeeper), @@ -204,13 +204,30 @@ func NewSimApp( app.mm.SetOrderInitGenesis( genaccounts.ModuleName, distr.ModuleName, staking.ModuleName, auth.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName, - mint.ModuleName, supply.ModuleName, crisis.ModuleName, genutil.ModuleName, + mint.ModuleName, supply.ModuleName, crisis.ModuleName, nft.ModuleName, + genutil.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) app.mm.RegisterRoutes(app.Router(), app.QueryRouter()) - app.sm = module.NewSimulationManager(app.mm.Modules) + // create the simulation manager and define the order of the modules for deterministic simulations + // + // NOTE: this is not required apps that don't use the simulator for fuzz testing + // transactions + app.sm = module.NewSimulationManager( + genaccounts.NewAppModule(app.AccountKeeper), + auth.NewAppModule(app.AccountKeeper), + bank.NewAppModule(app.BankKeeper, app.AccountKeeper), + supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper), + gov.NewAppModule(app.GovKeeper, app.SupplyKeeper), + mint.NewAppModule(app.MintKeeper), + distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), + staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), + slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), + // nft.NewAppModule(app.NFTKeeper), + ) + app.sm.RegisterStoreDecoders() // initialize stores diff --git a/simapp/doc.go b/simapp/doc.go deleted file mode 100644 index 1ed65eb9d6f9..000000000000 --- a/simapp/doc.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -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 \ - -Enabled=true \ - -NumBlocks=100 \ - -BlockSize=200 \ - -Commit=true \ - -Seed=99 \ - -Period=5 \ - -v -timeout 24h - -To execute simulation from a genesis file: - - $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ - -run=TestFullAppSimulation \ - -Enabled=true \ - -NumBlocks=100 \ - -BlockSize=200 \ - -Commit=true \ - -Seed=99 \ - -Period=5 \ - -Genesis=/path/to/genesis.json \ - -v -timeout 24h - -To execute simulation from a simulation params file: - - $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ - -run=TestFullAppSimulation \ - -Enabled=true \ - -NumBlocks=100 \ - -BlockSize=200 \ - -Commit=true \ - -Seed=99 \ - -Period=5 \ - -Params=/path/to/params.json \ - -v -timeout 24h - -To export the simulation params to a file at a given block height: - - $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ - -run=TestFullAppSimulation \ - -Enabled=true \ - -NumBlocks=100 \ - -BlockSize=200 \ - -Commit=true \ - -Seed=99 \ - -Period=5 \ - -ExportParamsPath=/path/to/params.json \ - -ExportParamsHeight=50 \ - -v -timeout 24h - - -To export the simulation app state (i.e genesis) to a file: - - $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ - -run=TestFullAppSimulation \ - -Enabled=true \ - -NumBlocks=100 \ - -BlockSize=200 \ - -Commit=true \ - -Seed=99 \ - -Period=5 \ - -ExportStatePath=/path/to/genesis.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/sim_test.go b/simapp/sim_test.go index 67d73931466c..20ec274f6b1a 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -24,7 +24,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations" "github.com/cosmos/cosmos-sdk/x/mint" - nftsimops "github.com/cosmos/cosmos-sdk/x/nft/simulation/operations" "github.com/cosmos/cosmos-sdk/x/params" paramsimops "github.com/cosmos/cosmos-sdk/x/params/simulation/operations" "github.com/cosmos/cosmos-sdk/x/simulation" @@ -35,23 +34,24 @@ import ( "github.com/cosmos/cosmos-sdk/x/supply" ) +// Get flags every time the simulator is run func init() { GetSimulatorFlags() } // TODO: add description func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedOperation { - - cdc := MakeCodec() ap := make(simulation.AppParams) + paramChanges := app.sm.GenerateParamChanges(config.Seed) + if config.ParamsFile != "" { bz, err := ioutil.ReadFile(config.ParamsFile) if err != nil { panic(err) } - cdc.MustUnmarshalJSON(bz, &ap) + app.cdc.MustUnmarshalJSON(bz, &ap) } // nolint: govet @@ -59,7 +59,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightDeductFee, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightDeductFee, &v, nil, func(_ *rand.Rand) { v = 5 }) @@ -70,7 +70,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgSend, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgSend, &v, nil, func(_ *rand.Rand) { v = 100 }) @@ -81,7 +81,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightSingleInputMsgMultiSend, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightSingleInputMsgMultiSend, &v, nil, func(_ *rand.Rand) { v = 10 }) @@ -92,7 +92,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgSetWithdrawAddress, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgSetWithdrawAddress, &v, nil, func(_ *rand.Rand) { v = 50 }) @@ -103,7 +103,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgWithdrawDelegationReward, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgWithdrawDelegationReward, &v, nil, func(_ *rand.Rand) { v = 50 }) @@ -114,7 +114,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgWithdrawValidatorCommission, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgWithdrawValidatorCommission, &v, nil, func(_ *rand.Rand) { v = 50 }) @@ -125,7 +125,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightSubmitVotingSlashingTextProposal, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingTextProposal, &v, nil, func(_ *rand.Rand) { v = 5 }) @@ -136,7 +136,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightSubmitVotingSlashingCommunitySpendProposal, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingCommunitySpendProposal, &v, nil, func(_ *rand.Rand) { v = 5 }) @@ -147,18 +147,18 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightSubmitVotingSlashingParamChangeProposal, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingParamChangeProposal, &v, nil, func(_ *rand.Rand) { v = 5 }) return v }(nil), - govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, paramsimops.SimulateParamChangeProposalContent), + govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, paramsimops.SimulateParamChangeProposalContent(paramChanges)), }, { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgDeposit, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgDeposit, &v, nil, func(_ *rand.Rand) { v = 100 }) @@ -169,7 +169,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgCreateValidator, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgCreateValidator, &v, nil, func(_ *rand.Rand) { v = 100 }) @@ -180,7 +180,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgEditValidator, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgEditValidator, &v, nil, func(_ *rand.Rand) { v = 5 }) @@ -191,7 +191,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgDelegate, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgDelegate, &v, nil, func(_ *rand.Rand) { v = 100 }) @@ -202,7 +202,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgUndelegate, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgUndelegate, &v, nil, func(_ *rand.Rand) { v = 100 }) @@ -213,7 +213,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgBeginRedelegate, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgBeginRedelegate, &v, nil, func(_ *rand.Rand) { v = 100 }) @@ -224,7 +224,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO { func(_ *rand.Rand) int { var v int - ap.GetOrGenerate(cdc, OpWeightMsgUnjail, &v, nil, + ap.GetOrGenerate(app.cdc, OpWeightMsgUnjail, &v, nil, func(_ *rand.Rand) { v = 100 }) @@ -232,50 +232,50 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO }(nil), slashingsimops.SimulateMsgUnjail(app.SlashingKeeper), }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(cdc, OpWeightMsgTransferNFT, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - nftsimops.SimulateMsgTransferNFT(app.NFTKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(cdc, OpWeightMsgEditNFTMetadata, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - nftsimops.SimulateMsgEditNFTMetadata(app.NFTKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(cdc, OpWeightMsgMintNFT, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - nftsimops.SimulateMsgMintNFT(app.NFTKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(cdc, OpWeightMsgBurnNFT, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - nftsimops.SimulateMsgBurnNFT(app.NFTKeeper), - }, + // { + // func(_ *rand.Rand) int { + // var v int + // ap.GetOrGenerate(app.cdc, OpWeightMsgTransferNFT, &v, nil, + // func(_ *rand.Rand) { + // v = 33 + // }) + // return v + // }(nil), + // nftsimops.SimulateMsgTransferNFT(app.NFTKeeper), + // }, + // { + // func(_ *rand.Rand) int { + // var v int + // ap.GetOrGenerate(app.cdc, OpWeightMsgEditNFTMetadata, &v, nil, + // func(_ *rand.Rand) { + // v = 5 + // }) + // return v + // }(nil), + // nftsimops.SimulateMsgEditNFTMetadata(app.NFTKeeper), + // }, + // { + // func(_ *rand.Rand) int { + // var v int + // ap.GetOrGenerate(app.cdc, OpWeightMsgMintNFT, &v, nil, + // func(_ *rand.Rand) { + // v = 10 + // }) + // return v + // }(nil), + // nftsimops.SimulateMsgMintNFT(app.NFTKeeper), + // }, + // { + // func(_ *rand.Rand) int { + // var v int + // ap.GetOrGenerate(app.cdc, OpWeightMsgBurnNFT, &v, nil, + // func(_ *rand.Rand) { + // v = 5 + // }) + // return v + // }(nil), + // nftsimops.SimulateMsgBurnNFT(app.NFTKeeper), + // }, } } @@ -310,37 +310,22 @@ func BenchmarkFullAppSimulation(b *testing.B) { // Run randomized simulation // TODO: parameterize numbers, save for a later PR - _, params, simErr := simulation.SimulateFromSeed( - b, os.Stdout, app.BaseApp, AppStateFn, + _, simParams, simErr := simulation.SimulateFromSeed( + b, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.sm), testAndRunTxs(app, config), invariants(app), app.ModuleAccountAddrs(), config, ) // export state and params before the simulation error is checked if config.ExportStatePath != "" { - fmt.Println("Exporting app state...") - appState, _, err := app.ExportAppStateAndValidators(false, nil) - if err != nil { - fmt.Println(err) - b.Fail() - } - err = ioutil.WriteFile(config.ExportStatePath, []byte(appState), 0644) - if err != nil { + if err := ExportStateToJSON(app, config.ExportStatePath); err != nil { fmt.Println(err) b.Fail() } } if config.ExportParamsPath != "" { - fmt.Println("Exporting simulation params...") - paramsBz, err := json.MarshalIndent(params, "", " ") - if err != nil { - fmt.Println(err) - b.Fail() - } - - err = ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0644) - if err != nil { + if err := ExportParamsToJSON(simParams, config.ExportParamsPath); err != nil { fmt.Println(err) b.Fail() } @@ -360,7 +345,7 @@ func BenchmarkFullAppSimulation(b *testing.B) { func TestFullAppSimulation(t *testing.T) { if !flagEnabledValue { - t.Skip("Skipping application simulation") + t.Skip("skipping application simulation") } var logger log.Logger @@ -385,29 +370,20 @@ func TestFullAppSimulation(t *testing.T) { require.Equal(t, "SimApp", app.Name()) // Run randomized simulation - _, params, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, AppStateFn, + _, simParams, simErr := simulation.SimulateFromSeed( + t, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.sm), testAndRunTxs(app, config), invariants(app), app.ModuleAccountAddrs(), config, ) // export state and params before the simulation error is checked if config.ExportStatePath != "" { - fmt.Println("Exporting app state...") - appState, _, err := app.ExportAppStateAndValidators(false, nil) - require.NoError(t, err) - - err = ioutil.WriteFile(config.ExportStatePath, []byte(appState), 0644) + err := ExportStateToJSON(app, config.ExportStatePath) require.NoError(t, err) } if config.ExportParamsPath != "" { - fmt.Println("Exporting simulation params...") - fmt.Println(params) - paramsBz, err := json.MarshalIndent(params, "", " ") - require.NoError(t, err) - - err = ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0644) + err := ExportParamsToJSON(simParams, config.ExportParamsPath) require.NoError(t, err) } @@ -424,7 +400,7 @@ func TestFullAppSimulation(t *testing.T) { func TestAppImportExport(t *testing.T) { if !flagEnabledValue { - t.Skip("Skipping application import/export simulation") + t.Skip("skipping application import/export simulation") } var logger log.Logger @@ -450,27 +426,19 @@ func TestAppImportExport(t *testing.T) { // Run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, AppStateFn, + t, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.sm), testAndRunTxs(app, config), invariants(app), app.ModuleAccountAddrs(), config, ) // export state and simParams before the simulation error is checked if config.ExportStatePath != "" { - fmt.Println("Exporting app state...") - appState, _, err := app.ExportAppStateAndValidators(false, nil) - require.NoError(t, err) - - err = ioutil.WriteFile(config.ExportStatePath, []byte(appState), 0644) + err := ExportStateToJSON(app, config.ExportStatePath) require.NoError(t, err) } if config.ExportParamsPath != "" { - fmt.Println("Exporting simulation params...") - simParamsBz, err := json.MarshalIndent(simParams, "", " ") - require.NoError(t, err) - - err = ioutil.WriteFile(config.ExportParamsPath, simParamsBz, 0644) + err := ExportParamsToJSON(simParams, config.ExportParamsPath) require.NoError(t, err) } @@ -484,11 +452,11 @@ func TestAppImportExport(t *testing.T) { fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) } - fmt.Printf("Exporting genesis...\n") + fmt.Printf("exporting genesis...\n") appState, _, err := app.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err) - fmt.Printf("Importing genesis...\n") + fmt.Printf("importing genesis...\n") newDir, _ := ioutil.TempDir("", "goleveldb-app-sim-2") newDB, _ := sdk.NewLevelDB("Simulation-2", dir) @@ -508,7 +476,7 @@ func TestAppImportExport(t *testing.T) { ctxB := newApp.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) newApp.mm.InitGenesis(ctxB, genesisState) - fmt.Printf("Comparing stores...\n") + fmt.Printf("comparing stores...\n") ctxA := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) type StoreKeysPrefixes struct { @@ -543,7 +511,7 @@ func TestAppImportExport(t *testing.T) { failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, prefixes) require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") - fmt.Printf("Compared %d key/value pairs between %s and %s\n", len(failedKVAs), storeKeyA, storeKeyB) + fmt.Printf("compared %d key/value pairs between %s and %s\n", len(failedKVAs), storeKeyA, storeKeyB) require.Len(t, failedKVAs, 0, GetSimulationLog(storeKeyA.Name(), app.sm.StoreDecoders, app.cdc, failedKVAs, failedKVBs)) } @@ -551,7 +519,7 @@ func TestAppImportExport(t *testing.T) { func TestAppSimulationAfterImport(t *testing.T) { if !flagEnabledValue { - t.Skip("Skipping application simulation after import") + t.Skip("skipping application simulation after import") } var logger log.Logger @@ -575,28 +543,20 @@ func TestAppSimulationAfterImport(t *testing.T) { require.Equal(t, "SimApp", app.Name()) // Run randomized simulation - stopEarly, params, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, AppStateFn, + stopEarly, simParams, simErr := simulation.SimulateFromSeed( + t, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.sm), testAndRunTxs(app, config), invariants(app), app.ModuleAccountAddrs(), config, ) // export state and params before the simulation error is checked if config.ExportStatePath != "" { - fmt.Println("Exporting app state...") - appState, _, err := app.ExportAppStateAndValidators(false, nil) - require.NoError(t, err) - - err = ioutil.WriteFile(config.ExportStatePath, []byte(appState), 0644) + err := ExportStateToJSON(app, config.ExportStatePath) require.NoError(t, err) } if config.ExportParamsPath != "" { - fmt.Println("Exporting simulation params...") - paramsBz, err := json.MarshalIndent(params, "", " ") - require.NoError(t, err) - - err = ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0644) + err := ExportParamsToJSON(simParams, config.ExportParamsPath) require.NoError(t, err) } @@ -612,16 +572,16 @@ func TestAppSimulationAfterImport(t *testing.T) { if stopEarly { // we can't export or import a zero-validator genesis - fmt.Printf("We can't export or import a zero-validator genesis, exiting test...\n") + fmt.Printf("we can't export or import a zero-validator genesis, exiting test...\n") return } - fmt.Printf("Exporting genesis...\n") + fmt.Printf("exporting genesis...\n") appState, _, err := app.ExportAppStateAndValidators(true, []string{}) require.NoError(t, err) - fmt.Printf("Importing genesis...\n") + fmt.Printf("importing genesis...\n") newDir, _ := ioutil.TempDir("", "goleveldb-app-sim-2") newDB, _ := sdk.NewLevelDB("Simulation-2", dir) @@ -639,7 +599,7 @@ func TestAppSimulationAfterImport(t *testing.T) { // Run randomized simulation on imported app _, _, err = simulation.SimulateFromSeed( - t, os.Stdout, newApp.BaseApp, AppStateFn, + t, os.Stdout, newApp.BaseApp, AppStateFn(app.Codec(), app.sm), testAndRunTxs(newApp, config), invariants(newApp), newApp.ModuleAccountAddrs(), config, ) @@ -651,7 +611,7 @@ func TestAppSimulationAfterImport(t *testing.T) { // and doesn't depend on the application. func TestAppStateDeterminism(t *testing.T) { if !flagEnabledValue { - t.Skip("Skipping application simulation") + t.Skip("skipping application simulation") } config := NewConfigFromFlags() @@ -673,12 +633,12 @@ func TestAppStateDeterminism(t *testing.T) { app := NewSimApp(logger, db, nil, true, 0) fmt.Printf( - "Running non-determinism simulation; seed: %d/%d (%d), attempt: %d/%d\n", - i+1, numSeeds, config.Seed, j+1, numTimesToRunPerSeed, + "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", + config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, ) _, _, err := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, AppStateFn, + t, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.sm), testAndRunTxs(app, config), []sdk.Invariant{}, app.ModuleAccountAddrs(), config, ) @@ -688,7 +648,10 @@ func TestAppStateDeterminism(t *testing.T) { appHashList[j] = appHash if j != 0 { - require.Equal(t, appHashList[0], appHashList[j], "appHash list: %v", appHashList) + require.Equal( + t, appHashList[0], appHashList[j], + "non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, + ) } } } @@ -696,7 +659,9 @@ func TestAppStateDeterminism(t *testing.T) { func BenchmarkInvariants(b *testing.B) { logger := log.NewNopLogger() + config := NewConfigFromFlags() + config.AllInvariants = false dir, _ := ioutil.TempDir("", "goleveldb-app-invariant-bench") db, _ := sdk.NewLevelDB("simulation", dir) @@ -709,37 +674,22 @@ func BenchmarkInvariants(b *testing.B) { app := NewSimApp(logger, db, nil, true, 0) // 2. Run parameterized simulation (w/o invariants) - _, params, simErr := simulation.SimulateFromSeed( - b, ioutil.Discard, app.BaseApp, AppStateFn, + _, simParams, simErr := simulation.SimulateFromSeed( + b, ioutil.Discard, app.BaseApp, AppStateFn(app.Codec(), app.sm), testAndRunTxs(app, config), []sdk.Invariant{}, app.ModuleAccountAddrs(), config, ) // export state and params before the simulation error is checked if config.ExportStatePath != "" { - fmt.Println("Exporting app state...") - appState, _, err := app.ExportAppStateAndValidators(false, nil) - if err != nil { - fmt.Println(err) - b.Fail() - } - err = ioutil.WriteFile(config.ExportStatePath, []byte(appState), 0644) - if err != nil { + if err := ExportStateToJSON(app, config.ExportStatePath); err != nil { fmt.Println(err) b.Fail() } } if config.ExportParamsPath != "" { - fmt.Println("Exporting simulation params...") - paramsBz, err := json.MarshalIndent(params, "", " ") - if err != nil { - fmt.Println(err) - b.Fail() - } - - err = ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0644) - if err != nil { + if err := ExportParamsToJSON(simParams, config.ExportParamsPath); err != nil { fmt.Println(err) b.Fail() } diff --git a/simapp/state.go b/simapp/state.go index c531a3320215..d048b83d4bcf 100644 --- a/simapp/state.go +++ b/simapp/state.go @@ -1,7 +1,5 @@ package simapp -// DONTCOVER - import ( "encoding/json" "fmt" @@ -12,71 +10,72 @@ import ( "github.com/tendermint/tendermint/crypto/secp256k1" tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/genaccounts" - nftsim "github.com/cosmos/cosmos-sdk/x/nft/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" ) // AppStateFn returns the initial application state using a genesis or the simulation parameters. // It panics if the user provides files for both of them. // If a file is not given for the genesis or the sim params, it creates a randomized one. -func AppStateFn( - r *rand.Rand, accs []simulation.Account, config simulation.Config, -) (appState json.RawMessage, simAccs []simulation.Account, chainID string, genesisTimestamp time.Time) { +func AppStateFn(cdc *codec.Codec, simManager *module.SimulationManager) simulation.AppStateFn { + return func(r *rand.Rand, accs []simulation.Account, config simulation.Config, + ) (appState json.RawMessage, simAccs []simulation.Account, chainID string, genesisTimestamp time.Time) { + + if flagGenesisTimeValue == 0 { + genesisTimestamp = simulation.RandTimestamp(r) + } else { + genesisTimestamp = time.Unix(flagGenesisTimeValue, 0) + } - cdc := MakeCodec() + switch { + case config.ParamsFile != "" && config.GenesisFile != "": + panic("cannot provide both a genesis file and a params file") - if flagGenesisTimeValue == 0 { - genesisTimestamp = simulation.RandTimestamp(r) - } else { - genesisTimestamp = time.Unix(flagGenesisTimeValue, 0) - } + case config.GenesisFile != "": + appState, simAccs, chainID = AppStateFromGenesisFileFn(r, cdc, config.GenesisFile) - switch { - case config.ParamsFile != "" && config.GenesisFile != "": - panic("cannot provide both a genesis file and a params file") + case config.ParamsFile != "": + appParams := make(simulation.AppParams) + bz, err := ioutil.ReadFile(config.ParamsFile) + if err != nil { + panic(err) + } - case config.GenesisFile != "": - appState, simAccs, chainID = AppStateFromGenesisFileFn(r, config) + cdc.MustUnmarshalJSON(bz, &appParams) + appState, simAccs, chainID = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) - case config.ParamsFile != "": - appParams := make(simulation.AppParams) - bz, err := ioutil.ReadFile(config.ParamsFile) - if err != nil { - panic(err) + default: + appParams := make(simulation.AppParams) + appState, simAccs, chainID = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) } - 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, genesisTimestamp } - - return appState, simAccs, chainID, genesisTimestamp } // AppStateRandomizedFn creates calls each module's GenesisState generator function -// and creates +// and creates the simulation params func AppStateRandomizedFn( - r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time, appParams simulation.AppParams, + simManager *module.SimulationManager, r *rand.Rand, cdc *codec.Codec, + accs []simulation.Account, genesisTimestamp time.Time, appParams simulation.AppParams, ) (json.RawMessage, []simulation.Account, string) { - - cdc := MakeCodec() + numAccs := int64(len(accs)) genesisState := NewDefaultGenesisState() - var ( - amount int64 - numInitiallyBonded int64 + // generate a random amount of initial stake coins and a random initial + // number of bonded accounts + var initialStake, numInitiallyBonded int64 + appParams.GetOrGenerate( + cdc, StakePerAccount, &initialStake, r, + func(r *rand.Rand) { initialStake = int64(r.Intn(1e12)) }, + ) + appParams.GetOrGenerate( + cdc, InitiallyBondedValidators, &numInitiallyBonded, r, + func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(250)) }, ) - 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)) }) - - numAccs := int64(len(accs)) if numInitiallyBonded > numAccs { numInitiallyBonded = numAccs } @@ -84,24 +83,26 @@ func AppStateRandomizedFn( fmt.Printf( `Selected randomly generated parameters for simulated genesis: { - stake_per_account: "%v", - initially_bonded_validators: "%v" + stake_per_account: "%d", + initially_bonded_validators: "%d" } -`, amount, numInitiallyBonded, +`, initialStake, numInitiallyBonded, ) - GenGenesisAccounts(cdc, r, accs, genesisTimestamp, amount, numInitiallyBonded, genesisState) - GenAuthGenesisState(cdc, r, appParams, genesisState) - GenBankGenesisState(cdc, r, appParams, genesisState) - GenSupplyGenesisState(cdc, amount, numInitiallyBonded, int64(len(accs)), genesisState) - GenGovGenesisState(cdc, r, appParams, genesisState) - GenMintGenesisState(cdc, r, appParams, genesisState) - nftsim.GenNFTGenesisState(cdc, r, accs, 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) + simState := &module.SimulationState{ + AppParams: appParams, + Cdc: cdc, + Rand: r, + GenState: genesisState, + Accounts: accs, + InitialStake: initialStake, + NumBonded: numInitiallyBonded, + GenTimestamp: genesisTimestamp, + } + + simManager.GenerateGenesisStates(simState) + + appState, err := cdc.MarshalJSON(genesisState) if err != nil { panic(err) } @@ -111,16 +112,15 @@ func AppStateRandomizedFn( // AppStateFromGenesisFileFn util function to generate the genesis AppState // from a genesis.json file -func AppStateFromGenesisFileFn(r *rand.Rand, config simulation.Config) (json.RawMessage, []simulation.Account, string) { - - var genesis tmtypes.GenesisDoc - cdc := MakeCodec() +func AppStateFromGenesisFileFn(r *rand.Rand, cdc *codec.Codec, genesisFile string) ( + genState json.RawMessage, newAccs []simulation.Account, chainID string) { - bytes, err := ioutil.ReadFile(config.GenesisFile) + bytes, err := ioutil.ReadFile(genesisFile) if err != nil { panic(err) } + var genesis tmtypes.GenesisDoc cdc.MustUnmarshalJSON(bytes, &genesis) var appState GenesisState @@ -128,16 +128,20 @@ func AppStateFromGenesisFileFn(r *rand.Rand, config simulation.Config) (json.Raw accounts := genaccounts.GetGenesisStateFromAppState(cdc, appState) - var newAccs []simulation.Account for _, acc := range accounts { // Pick a random private key, since we don't know the actual key // This should be fine as it's only used for mock Tendermint validators // and these keys are never actually used to sign by mock Tendermint. privkeySeed := make([]byte, 15) - r.Read(privkeySeed) + if _, err := r.Read(privkeySeed); err != nil { + panic(err) + } privKey := secp256k1.GenPrivKeySecp256k1(privkeySeed) - newAccs = append(newAccs, simulation.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: acc.Address}) + + // create simulator accounts + simAcc := simulation.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: acc.Address} + newAccs = append(newAccs, simAcc) } return genesis.AppState, newAccs, genesis.ChainID diff --git a/simapp/utils.go b/simapp/utils.go index b0bd4f26a382..443633ddc78a 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -1,33 +1,22 @@ -//nolint package simapp import ( "encoding/json" "flag" "fmt" - "math/rand" - "time" + "io/ioutil" cmn "github.com/tendermint/tendermint/libs/common" "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/bank" - "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/genaccounts" - "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" ) //--------------------------------------------------------------------- // Flags -// List of SimApp flags for the simulator +// List of available flags for the simulator var ( flagGenesisFileValue string flagParamsFileValue string @@ -96,405 +85,31 @@ func NewConfigFromFlags() simulation.Config { } } -// GenAuthGenesisState generates a random GenesisState for auth -func GenAuthGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams, genesisState map[string]json.RawMessage) { - authGenesis := auth.NewGenesisState( - 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) -} - -// GenBankGenesisState generates a random GenesisState for bank -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) -} - -// GenSupplyGenesisState generates a random GenesisState for supply -func GenSupplyGenesisState(cdc *codec.Codec, amount, numInitiallyBonded, numAccs int64, genesisState map[string]json.RawMessage) { - totalSupply := sdk.NewInt(amount * (numAccs + numInitiallyBonded)) - supplyGenesis := supply.NewGenesisState( - sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, totalSupply)), - ) - - fmt.Printf("Generated supply parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, supplyGenesis)) - genesisState[supply.ModuleName] = cdc.MustMarshalJSON(supplyGenesis) -} - -// GenGenesisAccounts generates a random GenesisState for the genesis accounts -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 { - coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(amount))} - bacc := auth.NewBaseAccountWithAddress(acc.Address) - bacc.SetCoins(coins) - - var gacc genaccounts.GenesisAccount - - // Only consider making a vesting account once the initial bonded validator - // set is exhausted due to needing to track DelegatedVesting. - if int64(i) > numInitiallyBonded && r.Intn(100) < 50 { - var ( - vacc auth.VestingAccount - endTime int64 - ) - - startTime := genesisTimestamp.Unix() - - // 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 { - endTime = int64(simulation.RandIntBetween(r, int(startTime), int(startTime+(60*60*12)))) - } - - if startTime == endTime { - endTime++ - } - - if r.Intn(100) < 50 { - vacc = auth.NewContinuousVestingAccount(&bacc, startTime, endTime) - } else { - vacc = auth.NewDelayedVestingAccount(&bacc, endTime) - } - - var err error - gacc, err = genaccounts.NewGenesisAccountI(vacc) - if err != nil { - panic(err) - } - } else { - gacc = genaccounts.NewGenesisAccount(&bacc) - } - - genesisAccounts = append(genesisAccounts, gacc) - } - - genesisState[genaccounts.ModuleName] = cdc.MustMarshalJSON(genesisAccounts) -} - -// GenGovGenesisState generates a random GenesisState for gov -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) - }) - - govGenesis := gov.NewGenesisState( - uint64(r.Intn(100)), - gov.NewDepositParams( - 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( - 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) -} - -// GenMintGenesisState generates a random GenesisState for mint -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) -} +//--------------------------------------------------------------------- +// Simulation Utils -// GenDistrGenesisState generates a random GenesisState for distribution -func GenDistrGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams, genesisState map[string]json.RawMessage) { - distrGenesis := distribution.GenesisState{ - FeePool: distribution.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), +// ExportStateToJSON util function to export the app state to JSON +func ExportStateToJSON(app *SimApp, path string) error { + fmt.Println("exporting app state...") + appState, _, err := app.ExportAppStateAndValidators(false, nil) + if err != nil { + return err } - fmt.Printf("Selected randomly generated distribution parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, distrGenesis)) - genesisState[distribution.ModuleName] = cdc.MustMarshalJSON(distrGenesis) -} - -// GenSlashingGenesisState generates a random GenesisState for slashing -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) + return ioutil.WriteFile(path, []byte(appState), 0644) } -// GenStakingGenesisState generates a random GenesisState for staking -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.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, - ), - nil, - nil, - ) - - var ( - validators []staking.Validator - delegations []staking.Delegation - ) - - valAddrs := make([]sdk.ValAddress, numInitiallyBonded) - for i := 0; i < int(numInitiallyBonded); i++ { - valAddr := sdk.ValAddress(accs[i].Address) - valAddrs[i] = valAddr - - validator := staking.NewValidator(valAddr, accs[i].PubKey, staking.Description{}) - validator.Tokens = sdk.NewInt(amount) - validator.DelegatorShares = sdk.NewDec(amount) - delegation := staking.NewDelegation(accs[i].Address, valAddr, sdk.NewDec(amount)) - validators = append(validators, validator) - delegations = append(delegations, delegation) +// ExportParamsToJSON util function to export the simulation parameters to JSON +func ExportParamsToJSON(params simulation.Params, path string) error { + fmt.Println("exporting simulation params...") + paramsBz, err := json.MarshalIndent(params, "", " ") + if err != nil { + return err } - 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) - - return stakingGenesis + return ioutil.WriteFile(path, paramsBz, 0644) } -//--------------------------------------------------------------------- -// Simulation Utils - // GetSimulationLog unmarshals the KVPair's Value to the corresponding type based on the // each's module store key and the prefix bytes of the KVPair's key. func GetSimulationLog(storeName string, sdr sdk.StoreDecoderRegistry, cdc *codec.Codec, kvAs, kvBs []cmn.KVPair) (log string) { diff --git a/simapp/utils_test.go b/simapp/utils_test.go index ed055f1d9bfa..d1bc9db3d9d9 100644 --- a/simapp/utils_test.go +++ b/simapp/utils_test.go @@ -15,16 +15,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func makeTestCodec() (cdc *codec.Codec) { - cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - auth.RegisterCodec(cdc) - return -} - func TestGetSimulationLog(t *testing.T) { - cdc := makeTestCodec() + cdc := MakeCodec() decoders := make(sdk.StoreDecoderRegistry) decoders[auth.StoreKey] = func(cdc *codec.Codec, kvAs, kvBs cmn.KVPair) string { return "10" } diff --git a/types/module/module.go b/types/module/module.go index c410bcf1055c..89b8d9271548 100644 --- a/types/module/module.go +++ b/types/module/module.go @@ -130,17 +130,9 @@ type AppModuleGenesis interface { ExportGenesis(sdk.Context) json.RawMessage } -// AppModuleSimulation defines the standard functions that every module should expose -// for the SDK blockchain simulator -type AppModuleSimulation interface { - // register a func to decode the each module's defined types from their corresponding store key - RegisterStoreDecoder(sdk.StoreDecoderRegistry) -} - // AppModule is the standard form for an application module type AppModule interface { AppModuleGenesis - AppModuleSimulation // registers RegisterInvariants(sdk.InvariantRegistry) @@ -173,9 +165,6 @@ func NewGenesisOnlyAppModule(amg AppModuleGenesis) AppModule { // RegisterInvariants is a placeholder function register no invariants func (GenesisOnlyAppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// RegisterStoreDecoder empty store decoder registry -func (GenesisOnlyAppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} - // Route empty module message route func (GenesisOnlyAppModule) Route() string { return "" } diff --git a/types/module/simulation.go b/types/module/simulation.go index c6901677dc2e..1d359888646b 100644 --- a/types/module/simulation.go +++ b/types/module/simulation.go @@ -1,20 +1,41 @@ package module import ( + "encoding/json" + "math/rand" + "time" + + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/simulation" ) +// AppModuleSimulation defines the standard functions that every module should expose +// for the SDK blockchain simulator +type AppModuleSimulation interface { + // register a func to decode the each module's defined types from their corresponding store key + RegisterStoreDecoder(sdk.StoreDecoderRegistry) + + // randomized genesis states + GenerateGenesisState(input *SimulationState) + + // randomized module parameters for param change proposals + RandomizedParams(r *rand.Rand) []simulation.ParamChange +} + // SimulationManager defines a simulation manager that provides the high level utility // for managing and executing simulation functionalities for a group of modules type SimulationManager struct { - Modules map[string]AppModule - StoreDecoders sdk.StoreDecoderRegistry + Modules []AppModuleSimulation // array of app modules; we use an array for deterministic simulation tests + StoreDecoders sdk.StoreDecoderRegistry // functions to decode the key-value pairs from each module's store } // NewSimulationManager creates a new SimulationManager object -func NewSimulationManager(moduleMap map[string]AppModule) *SimulationManager { +// +// CONTRACT: All the modules provided must be also registered on the module Manager +func NewSimulationManager(modules ...AppModuleSimulation) *SimulationManager { return &SimulationManager{ - Modules: moduleMap, + Modules: modules, StoreDecoders: make(sdk.StoreDecoderRegistry), } } @@ -25,3 +46,37 @@ func (sm *SimulationManager) RegisterStoreDecoders() { module.RegisterStoreDecoder(sm.StoreDecoders) } } + +// GenerateGenesisStates generates a randomized GenesisState for each of the +// registered modules +func (sm *SimulationManager) GenerateGenesisStates(input *SimulationState) { + for _, module := range sm.Modules { + module.GenerateGenesisState(input) + } +} + +// GenerateParamChanges generates randomized contents for creating params change +// proposal transactions +func (sm *SimulationManager) GenerateParamChanges(seed int64) (paramChanges []simulation.ParamChange) { + r := rand.New(rand.NewSource(seed)) + + for _, module := range sm.Modules { + paramChanges = append(paramChanges, module.RandomizedParams(r)...) + } + + return +} + +// SimulationState is the input parameters used on each of the module's randomized +// GenesisState generator function +type SimulationState struct { + AppParams simulation.AppParams + Cdc *codec.Codec // application codec + Rand *rand.Rand // random number + GenState map[string]json.RawMessage // genesis state + Accounts []simulation.Account // simulation accounts + InitialStake int64 // initial coins per account + NumBonded int64 // number of initially bonded acconts + GenTimestamp time.Time // genesis timestamp + UnbondTime time.Duration // staking unbond time stored to use it as the slashing maximum evidence duration +} diff --git a/x/auth/module.go b/x/auth/module.go index cb45b4913dc0..676a54b61990 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -2,6 +2,7 @@ package auth import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -15,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/auth/simulation" "github.com/cosmos/cosmos-sdk/x/auth/types" + sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -77,6 +79,16 @@ func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[StoreKey] = simulation.DecodeStore } +// GenerateGenesisState creates a randomized GenState of the auth module +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams creates randomized auth param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + //____________________________________________________________________________ // AppModule implements an application module for the auth module. diff --git a/x/auth/simulation/genesis.go b/x/auth/simulation/genesis.go new file mode 100644 index 000000000000..69e85961da63 --- /dev/null +++ b/x/auth/simulation/genesis.go @@ -0,0 +1,88 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +// Simulation parameter constants +const ( + MaxMemoChars = "max_memo_characters" + TxSigLimit = "tx_sig_limit" + TxSizeCostPerByte = "tx_size_cost_per_byte" + SigVerifyCostED25519 = "sig_verify_cost_ed25519" + SigVerifyCostSECP256K1 = "sig_verify_cost_secp256k1" +) + +// GenMaxMemoChars randomized MaxMemoChars +func GenMaxMemoChars(r *rand.Rand) uint64 { + return uint64(simulation.RandIntBetween(r, 100, 200)) +} + +// GenTxSigLimit randomized TxSigLimit +func GenTxSigLimit(r *rand.Rand) uint64 { + return uint64(r.Intn(7) + 1) +} + +// GenTxSizeCostPerByte randomized TxSizeCostPerByte +func GenTxSizeCostPerByte(r *rand.Rand) uint64 { + return uint64(simulation.RandIntBetween(r, 5, 15)) +} + +// GenSigVerifyCostED25519 randomized SigVerifyCostED25519 +func GenSigVerifyCostED25519(r *rand.Rand) uint64 { + return uint64(simulation.RandIntBetween(r, 500, 1000)) +} + +// GenSigVerifyCostSECP256K1 randomized SigVerifyCostSECP256K1 +func GenSigVerifyCostSECP256K1(r *rand.Rand) uint64 { + return uint64(simulation.RandIntBetween(r, 500, 1000)) +} + +// RandomizedGenState generates a random GenesisState for auth +func RandomizedGenState(simState *module.SimulationState) { + var maxMemoChars uint64 + simState.AppParams.GetOrGenerate( + simState.Cdc, MaxMemoChars, &maxMemoChars, simState.Rand, + func(r *rand.Rand) { maxMemoChars = GenMaxMemoChars(r) }, + ) + + var txSigLimit uint64 + simState.AppParams.GetOrGenerate( + simState.Cdc, TxSigLimit, &txSigLimit, simState.Rand, + func(r *rand.Rand) { txSigLimit = GenTxSigLimit(r) }, + ) + + var txSizeCostPerByte uint64 + simState.AppParams.GetOrGenerate( + simState.Cdc, TxSizeCostPerByte, &txSizeCostPerByte, simState.Rand, + func(r *rand.Rand) { txSizeCostPerByte = GenTxSizeCostPerByte(r) }, + ) + + var sigVerifyCostED25519 uint64 + simState.AppParams.GetOrGenerate( + simState.Cdc, SigVerifyCostED25519, &sigVerifyCostED25519, simState.Rand, + func(r *rand.Rand) { sigVerifyCostED25519 = GenSigVerifyCostED25519(r) }, + ) + + var sigVerifyCostSECP256K1 uint64 + simState.AppParams.GetOrGenerate( + simState.Cdc, SigVerifyCostSECP256K1, &sigVerifyCostSECP256K1, simState.Rand, + func(r *rand.Rand) { sigVerifyCostED25519 = GenSigVerifyCostSECP256K1(r) }, + ) + + authGenesis := types.NewGenesisState( + types.NewParams(maxMemoChars, txSigLimit, txSizeCostPerByte, + sigVerifyCostED25519, sigVerifyCostSECP256K1), + ) + + fmt.Printf("Selected randomly generated auth parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, authGenesis.Params)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis) +} diff --git a/x/auth/simulation/operations/fake.go b/x/auth/simulation/operations/msgs.go similarity index 100% rename from x/auth/simulation/operations/fake.go rename to x/auth/simulation/operations/msgs.go diff --git a/x/auth/simulation/params.go b/x/auth/simulation/params.go new file mode 100644 index 000000000000..045124a4a4a3 --- /dev/null +++ b/x/auth/simulation/params.go @@ -0,0 +1,39 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +const ( + keyMaxMemoCharacters = "MaxMemoCharacters" + keyTxSigLimit = "TxSigLimit" + keyTxSizeCostPerByte = "TxSizeCostPerByte" +) + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation +func ParamChanges(r *rand.Rand) []simulation.ParamChange { + return []simulation.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, keyMaxMemoCharacters, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%d\"", GenMaxMemoChars(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyTxSigLimit, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%d\"", GenTxSigLimit(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyTxSizeCostPerByte, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%d\"", GenTxSizeCostPerByte(r)) + }, + ), + } +} diff --git a/x/bank/alias.go b/x/bank/alias.go index b4885a675837..662a408ce38b 100644 --- a/x/bank/alias.go +++ b/x/bank/alias.go @@ -1,7 +1,8 @@ // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/bank/types +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/bank/internal/keeper +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/bank/internal/types package bank import ( @@ -10,26 +11,40 @@ import ( ) const ( + QueryBalance = keeper.QueryBalance DefaultCodespace = types.DefaultCodespace CodeSendDisabled = types.CodeSendDisabled CodeInvalidInputsOutputs = types.CodeInvalidInputsOutputs ModuleName = types.ModuleName - RouterKey = types.RouterKey QuerierRoute = types.QuerierRoute + RouterKey = types.RouterKey DefaultParamspace = types.DefaultParamspace + DefaultSendEnabled = types.DefaultSendEnabled ) var ( // functions aliases - RegisterCodec = types.RegisterCodec - ErrNoInputs = types.ErrNoInputs - ErrNoOutputs = types.ErrNoOutputs - ErrInputOutputMismatch = types.ErrInputOutputMismatch - ErrSendDisabled = types.ErrSendDisabled - NewBaseKeeper = keeper.NewBaseKeeper - NewInput = types.NewInput - NewOutput = types.NewOutput - ParamKeyTable = types.ParamKeyTable + RegisterInvariants = keeper.RegisterInvariants + NonnegativeBalanceInvariant = keeper.NonnegativeBalanceInvariant + NewBaseKeeper = keeper.NewBaseKeeper + NewBaseSendKeeper = keeper.NewBaseSendKeeper + NewBaseViewKeeper = keeper.NewBaseViewKeeper + NewQuerier = keeper.NewQuerier + RegisterCodec = types.RegisterCodec + ErrNoInputs = types.ErrNoInputs + ErrNoOutputs = types.ErrNoOutputs + ErrInputOutputMismatch = types.ErrInputOutputMismatch + ErrSendDisabled = types.ErrSendDisabled + NewGenesisState = types.NewGenesisState + DefaultGenesisState = types.DefaultGenesisState + ValidateGenesis = types.ValidateGenesis + NewMsgSend = types.NewMsgSend + NewMsgMultiSend = types.NewMsgMultiSend + NewInput = types.NewInput + NewOutput = types.NewOutput + ValidateInputsOutputs = types.ValidateInputsOutputs + ParamKeyTable = types.ParamKeyTable + NewQueryBalanceParams = types.NewQueryBalanceParams // variable aliases ModuleCdc = types.ModuleCdc @@ -37,10 +52,16 @@ var ( ) type ( - BaseKeeper = keeper.BaseKeeper // ibc module depends on this - Keeper = keeper.Keeper - MsgSend = types.MsgSend - MsgMultiSend = types.MsgMultiSend - Input = types.Input - Output = types.Output + Keeper = keeper.Keeper + BaseKeeper = keeper.BaseKeeper + SendKeeper = keeper.SendKeeper + BaseSendKeeper = keeper.BaseSendKeeper + ViewKeeper = keeper.ViewKeeper + BaseViewKeeper = keeper.BaseViewKeeper + GenesisState = types.GenesisState + MsgSend = types.MsgSend + MsgMultiSend = types.MsgMultiSend + Input = types.Input + Output = types.Output + QueryBalanceParams = types.QueryBalanceParams ) diff --git a/x/bank/genesis.go b/x/bank/genesis.go index bfd08153bbec..641875e6dc88 100644 --- a/x/bank/genesis.go +++ b/x/bank/genesis.go @@ -4,19 +4,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// GenesisState is the bank state that must be provided at genesis. -type GenesisState struct { - SendEnabled bool `json:"send_enabled" yaml:"send_enabled"` -} - -// NewGenesisState creates a new genesis state. -func NewGenesisState(sendEnabled bool) GenesisState { - return GenesisState{SendEnabled: sendEnabled} -} - -// DefaultGenesisState returns a default genesis state -func DefaultGenesisState() GenesisState { return NewGenesisState(true) } - // InitGenesis sets distribution information for genesis. func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { keeper.SetSendEnabled(ctx, data.SendEnabled) @@ -26,7 +13,3 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { return NewGenesisState(keeper.GetSendEnabled(ctx)) } - -// ValidateGenesis performs basic validation of bank genesis data returning an -// error for any failed validation criteria. -func ValidateGenesis(data GenesisState) error { return nil } diff --git a/x/bank/internal/types/genesis.go b/x/bank/internal/types/genesis.go new file mode 100644 index 000000000000..159b04f1f9a4 --- /dev/null +++ b/x/bank/internal/types/genesis.go @@ -0,0 +1,18 @@ +package types + +// GenesisState is the bank state that must be provided at genesis. +type GenesisState struct { + SendEnabled bool `json:"send_enabled" yaml:"send_enabled"` +} + +// NewGenesisState creates a new genesis state. +func NewGenesisState(sendEnabled bool) GenesisState { + return GenesisState{SendEnabled: sendEnabled} +} + +// DefaultGenesisState returns a default genesis state +func DefaultGenesisState() GenesisState { return NewGenesisState(true) } + +// ValidateGenesis performs basic validation of bank genesis data returning an +// error for any failed validation criteria. +func ValidateGenesis(data GenesisState) error { return nil } diff --git a/x/bank/module.go b/x/bank/module.go index c2545036a758..2539f052cd3b 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -2,6 +2,7 @@ package bank import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -16,6 +17,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank/client/rest" "github.com/cosmos/cosmos-sdk/x/bank/internal/keeper" "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + "github.com/cosmos/cosmos-sdk/x/bank/simulation" + sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -70,6 +73,16 @@ type AppModuleSimulation struct{} // RegisterStoreDecoder performs a no-op. func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} +// GenerateGenesisState creates a randomized GenState of the bank module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams creates randomized bank param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + //____________________________________________________________________________ // AppModule implements an application module for the bank module. diff --git a/x/bank/simulation/genesis.go b/x/bank/simulation/genesis.go new file mode 100644 index 000000000000..5e676e80cfb8 --- /dev/null +++ b/x/bank/simulation/genesis.go @@ -0,0 +1,36 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/bank/internal/types" +) + +// Simulation parameter constants +const ( + SendEnabled = "send_enabled" +) + +// GenSendEnabled randomized SendEnabled +func GenSendEnabled(r *rand.Rand) bool { + return r.Int63n(2) == 0 +} + +// RandomizedGenState generates a random GenesisState for bank +func RandomizedGenState(simState *module.SimulationState) { + var sendEnabled bool + simState.AppParams.GetOrGenerate( + simState.Cdc, SendEnabled, &sendEnabled, simState.Rand, + func(r *rand.Rand) { sendEnabled = GenSendEnabled(r) }, + ) + + bankGenesis := types.NewGenesisState(sendEnabled) + + fmt.Printf("Selected randomly generated bank parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, bankGenesis)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(bankGenesis) +} diff --git a/x/bank/simulation/params.go b/x/bank/simulation/params.go new file mode 100644 index 000000000000..f0dfec04b0b2 --- /dev/null +++ b/x/bank/simulation/params.go @@ -0,0 +1,25 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +const keySendEnabled = "sendenabled" + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation +func ParamChanges(r *rand.Rand) []simulation.ParamChange { + return []simulation.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, keySendEnabled, "", + func(r *rand.Rand) string { + return fmt.Sprintf("%v", GenSendEnabled(r)) + }, + ), + } +} diff --git a/x/crisis/module.go b/x/crisis/module.go index ec84c736d3c8..4318c516cea8 100644 --- a/x/crisis/module.go +++ b/x/crisis/module.go @@ -18,9 +18,8 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} ) // AppModuleBasic defines the basic application module used by the crisis module. @@ -64,18 +63,9 @@ func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } //____________________________________________________________________________ -// AppModuleSimulation defines the module simulation functions used by the crisis module. -type AppModuleSimulation struct{} - -// RegisterStoreDecoder performs a no-op. -func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} - -//____________________________________________________________________________ - // AppModule implements an application module for the crisis module. type AppModule struct { AppModuleBasic - AppModuleSimulation // NOTE: We store a reference to the keeper here so that after a module // manager is created, the invariants can be properly registered and @@ -86,9 +76,8 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper *keeper.Keeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - AppModuleSimulation: AppModuleSimulation{}, - keeper: keeper, + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, } } diff --git a/x/distribution/module.go b/x/distribution/module.go index c156e67aad49..2b42e0b68c51 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -2,6 +2,7 @@ package distribution import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -16,6 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" "github.com/cosmos/cosmos-sdk/x/distribution/simulation" "github.com/cosmos/cosmos-sdk/x/distribution/types" + sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -78,6 +80,16 @@ func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[StoreKey] = simulation.DecodeStore } +// GenerateGenesisState creates a randomized GenState of the distribution module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams creates randomized distribution param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + //____________________________________________________________________________ // AppModule implements an application module for the distribution module. diff --git a/x/distribution/simulation/genesis.go b/x/distribution/simulation/genesis.go new file mode 100644 index 000000000000..e11213dad7ca --- /dev/null +++ b/x/distribution/simulation/genesis.go @@ -0,0 +1,67 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/codec" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// Simulation parameter constants +const ( + CommunityTax = "community_tax" + BaseProposerReward = "base_proposer_reward" + BonusProposerReward = "bonus_proposer_reward" +) + +// GenCommunityTax randomized CommunityTax +func GenCommunityTax(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)) +} + +// GenBaseProposerReward randomized BaseProposerReward +func GenBaseProposerReward(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)) +} + +// GenBonusProposerReward randomized BonusProposerReward +func GenBonusProposerReward(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)) +} + +// RandomizedGenState generates a random GenesisState for distribution +func RandomizedGenState(simState *module.SimulationState) { + var communityTax sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, CommunityTax, &communityTax, simState.Rand, + func(r *rand.Rand) { communityTax = GenCommunityTax(r) }, + ) + + var baseProposerReward sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, BaseProposerReward, &baseProposerReward, simState.Rand, + func(r *rand.Rand) { baseProposerReward = GenBaseProposerReward(r) }, + ) + + var bonusProposerReward sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, BonusProposerReward, &bonusProposerReward, simState.Rand, + func(r *rand.Rand) { bonusProposerReward = GenBonusProposerReward(r) }, + ) + + distrGenesis := types.GenesisState{ + FeePool: types.InitialFeePool(), + CommunityTax: communityTax, + BaseProposerReward: baseProposerReward, + BonusProposerReward: bonusProposerReward, + } + + fmt.Printf("Selected randomly generated distribution parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, distrGenesis)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(distrGenesis) +} diff --git a/x/distribution/simulation/operations/msgs.go b/x/distribution/simulation/operations/msgs.go index 6d30aac278c3..10bc0418fd59 100644 --- a/x/distribution/simulation/operations/msgs.go +++ b/x/distribution/simulation/operations/msgs.go @@ -7,8 +7,8 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/gov" govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -88,18 +88,22 @@ func SimulateMsgWithdrawValidatorCommission(k distribution.Keeper) simulation.Op // SimulateCommunityPoolSpendProposalContent generates random community-pool-spend proposal content func SimulateCommunityPoolSpendProposalContent(k distribution.Keeper) govsimops.ContentSimulator { - return func(r *rand.Rand, _ *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) gov.Content { + return func(r *rand.Rand, ctx sdk.Context, accs []simulation.Account) govtypes.Content { + var coins sdk.Coins + recipientAcc := simulation.RandomAcc(r, accs) - coins := sdk.Coins{} balance := k.GetFeePool(ctx).CommunityPool + if len(balance) > 0 { denomIndex := r.Intn(len(balance)) - amount, goErr := simulation.RandPositiveInt(r, balance[denomIndex].Amount.TruncateInt()) - if goErr == nil { + amount, err := simulation.RandPositiveInt(r, balance[denomIndex].Amount.TruncateInt()) + + if err == nil { denom := balance[denomIndex].Denom coins = sdk.NewCoins(sdk.NewCoin(denom, amount.Mul(sdk.NewInt(2)))) } } + return distribution.NewCommunityPoolSpendProposal( simulation.RandStringOfLength(r, 10), simulation.RandStringOfLength(r, 100), diff --git a/x/distribution/simulation/params.go b/x/distribution/simulation/params.go new file mode 100644 index 000000000000..710243e6d033 --- /dev/null +++ b/x/distribution/simulation/params.go @@ -0,0 +1,39 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +const ( + keyCommunityTax = "communitytax" + keyBaseProposerReward = "baseproposerreward" + keyBonusProposerReward = "bonusproposerreward" +) + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation +func ParamChanges(r *rand.Rand) []simulation.ParamChange { + return []simulation.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, keyCommunityTax, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenCommunityTax(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyBaseProposerReward, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenBaseProposerReward(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyBonusProposerReward, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenBonusProposerReward(r)) + }, + ), + } +} diff --git a/x/genaccounts/module.go b/x/genaccounts/module.go index 9a1f625aa96b..2b5d4871cb9b 100644 --- a/x/genaccounts/module.go +++ b/x/genaccounts/module.go @@ -2,6 +2,7 @@ package genaccounts import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -14,6 +15,8 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/cosmos/cosmos-sdk/x/genaccounts/internal/types" + "github.com/cosmos/cosmos-sdk/x/genaccounts/simulation" + sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -82,6 +85,16 @@ type AppModuleSimulation struct{} // RegisterStoreDecoder performs a no-op. func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} +// GenerateGenesisState creates a randomized GenState of the genesis accounts module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams doesn't create randomized genaccounts param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(_ *rand.Rand) []sim.ParamChange { + return nil +} + //____________________________________________________________________________ // AppModule implements an application module for the genesis accounts module. @@ -93,15 +106,30 @@ type AppModule struct { } // NewAppModule creates a new AppModule object -func NewAppModule(accountKeeper types.AccountKeeper) module.AppModule { +func NewAppModule(accountKeeper types.AccountKeeper) AppModule { - return module.NewGenesisOnlyAppModule(AppModule{ + return AppModule{ AppModuleBasic: AppModuleBasic{}, AppModuleSimulation: AppModuleSimulation{}, accountKeeper: accountKeeper, - }) + } } +// RegisterInvariants is a placeholder function register no invariants +func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// Route empty module message route +func (AppModule) Route() string { return "" } + +// NewHandler returns an empty module handler +func (AppModule) NewHandler() sdk.Handler { return nil } + +// QuerierRoute returns an empty module querier route +func (AppModule) QuerierRoute() string { return "" } + +// NewQuerierHandler returns an empty module querier +func (AppModule) NewQuerierHandler() sdk.Querier { return nil } + // InitGenesis performs genesis initialization for the genesis accounts module. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { @@ -117,3 +145,11 @@ func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.accountKeeper) return ModuleCdc.MustMarshalJSON(gs) } + +// BeginBlock returns an empty module begin-block +func (AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} + +// EndBlock returns an empty module end-block +func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/genaccounts/simulation/genesis.go b/x/genaccounts/simulation/genesis.go new file mode 100644 index 000000000000..ec7b0a405855 --- /dev/null +++ b/x/genaccounts/simulation/genesis.go @@ -0,0 +1,68 @@ +package simulation + +// DONTCOVER + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/genaccounts/internal/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +// RandomizedGenState generates a random GenesisState for the genesis accounts +func RandomizedGenState(simState *module.SimulationState) { + var genesisAccounts []types.GenesisAccount + + // randomly generate some genesis accounts + for i, acc := range simState.Accounts { + coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))} + bacc := authtypes.NewBaseAccountWithAddress(acc.Address) + if err := bacc.SetCoins(coins); err != nil { + panic(err) + } + + var gacc types.GenesisAccount + + // Only consider making a vesting account once the initial bonded validator + // set is exhausted due to needing to track DelegatedVesting. + if int64(i) > simState.NumBonded && simState.Rand.Intn(100) < 50 { + var ( + vacc authexported.VestingAccount + endTime int64 + ) + + startTime := simState.GenTimestamp.Unix() + + // Allow for some vesting accounts to vest very quickly while others very slowly. + if simState.Rand.Intn(100) < 50 { + endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime), int(startTime+(60*60*24*30)))) + } else { + endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime), int(startTime+(60*60*12)))) + } + + if startTime == endTime { + endTime++ + } + + if simState.Rand.Intn(100) < 50 { + vacc = authtypes.NewContinuousVestingAccount(&bacc, startTime, endTime) + } else { + vacc = authtypes.NewDelayedVestingAccount(&bacc, endTime) + } + + var err error + gacc, err = types.NewGenesisAccountI(vacc) + if err != nil { + panic(err) + } + } else { + gacc = types.NewGenesisAccount(&bacc) + } + + genesisAccounts = append(genesisAccounts, gacc) + } + + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesisAccounts) +} diff --git a/x/genutil/module.go b/x/genutil/module.go index 63e0a93a4acc..8d8f296ae1c9 100644 --- a/x/genutil/module.go +++ b/x/genutil/module.go @@ -16,9 +16,8 @@ import ( ) var ( - _ module.AppModuleGenesis = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModuleGenesis = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} ) // AppModuleBasic defines the basic application module used by the genutil module. @@ -59,18 +58,9 @@ func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } //____________________________________________________________________________ -// AppModuleSimulation defines the module simulation functions used by the auth module. -type AppModuleSimulation struct{} - -// RegisterStoreDecoder registers a decoder for genutil module's types -func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} - -//____________________________________________________________________________ - // AppModule implements an application module for the genutil module. type AppModule struct { AppModuleBasic - AppModuleSimulation accountKeeper types.AccountKeeper stakingKeeper types.StakingKeeper @@ -82,11 +72,10 @@ func NewAppModule(accountKeeper types.AccountKeeper, stakingKeeper types.StakingKeeper, deliverTx deliverTxfn) module.AppModule { return module.NewGenesisOnlyAppModule(AppModule{ - AppModuleBasic: AppModuleBasic{}, - AppModuleSimulation: AppModuleSimulation{}, - accountKeeper: accountKeeper, - stakingKeeper: stakingKeeper, - deliverTx: deliverTx, + AppModuleBasic: AppModuleBasic{}, + accountKeeper: accountKeeper, + stakingKeeper: stakingKeeper, + deliverTx: deliverTx, }) } diff --git a/x/gov/module.go b/x/gov/module.go index 81100e0ffae9..bbcd22498a7b 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -4,6 +4,7 @@ package gov import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -19,6 +20,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov/client/rest" "github.com/cosmos/cosmos-sdk/x/gov/simulation" "github.com/cosmos/cosmos-sdk/x/gov/types" + sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -96,11 +98,21 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { // AppModuleSimulation defines the module simulation functions used by the gov module. type AppModuleSimulation struct{} -// RegisterStoreDecoder performs a no-op. +// RegisterStoreDecoder registers a decoder for gov module's types func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[StoreKey] = simulation.DecodeStore } +// GenerateGenesisState creates a randomized GenState of the gov module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams creates randomized gov param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + //____________________________________________________________________________ // AppModule implements an application module for the gov module. diff --git a/x/gov/simulation/genesis.go b/x/gov/simulation/genesis.go new file mode 100644 index 000000000000..f2541643bd36 --- /dev/null +++ b/x/gov/simulation/genesis.go @@ -0,0 +1,107 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +// Simulation parameter constants +const ( + DepositParamsMinDeposit = "deposit_params_min_deposit" + DepositParamsDepositPeriod = "deposit_params_deposit_period" + VotingParamsVotingPeriod = "voting_params_voting_period" + TallyParamsQuorum = "tally_params_quorum" + TallyParamsThreshold = "tally_params_threshold" + TallyParamsVeto = "tally_params_veto" +) + +// GenDepositParamsDepositPeriod randomized DepositParamsDepositPeriod +func GenDepositParamsDepositPeriod(r *rand.Rand) time.Duration { + return time.Duration(simulation.RandIntBetween(r, 1, 2*60*60*24*2)) * time.Second +} + +// GenDepositParamsMinDeposit randomized DepositParamsMinDeposit +func GenDepositParamsMinDeposit(r *rand.Rand) sdk.Coins { + return sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, int64(simulation.RandIntBetween(r, 1, 1e3)))) +} + +// GenVotingParamsVotingPeriod randomized VotingParamsVotingPeriod +func GenVotingParamsVotingPeriod(r *rand.Rand) time.Duration { + return time.Duration(simulation.RandIntBetween(r, 1, 2*60*60*24*2)) * time.Second +} + +// GenTallyParamsQuorum randomized TallyParamsQuorum +func GenTallyParamsQuorum(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(int64(simulation.RandIntBetween(r, 334, 500)), 3) +} + +// GenTallyParamsThreshold randomized TallyParamsThreshold +func GenTallyParamsThreshold(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(int64(simulation.RandIntBetween(r, 450, 550)), 3) +} + +// GenTallyParamsVeto randomized TallyParamsVeto +func GenTallyParamsVeto(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(int64(simulation.RandIntBetween(r, 250, 334)), 3) +} + +// RandomizedGenState generates a random GenesisState for gov +func RandomizedGenState(simState *module.SimulationState) { + startingProposalID := uint64(simState.Rand.Intn(100)) + + var minDeposit sdk.Coins + simState.AppParams.GetOrGenerate( + simState.Cdc, DepositParamsMinDeposit, &minDeposit, simState.Rand, + func(r *rand.Rand) { minDeposit = GenDepositParamsMinDeposit(r) }, + ) + + var depositPeriod time.Duration + simState.AppParams.GetOrGenerate( + simState.Cdc, DepositParamsDepositPeriod, &depositPeriod, simState.Rand, + func(r *rand.Rand) { depositPeriod = GenDepositParamsDepositPeriod(r) }, + ) + + var votingPeriod time.Duration + simState.AppParams.GetOrGenerate( + simState.Cdc, VotingParamsVotingPeriod, &votingPeriod, simState.Rand, + func(r *rand.Rand) { votingPeriod = GenVotingParamsVotingPeriod(r) }, + ) + + var quorum sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, TallyParamsQuorum, &quorum, simState.Rand, + func(r *rand.Rand) { quorum = GenTallyParamsQuorum(r) }, + ) + + var threshold sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, TallyParamsThreshold, &threshold, simState.Rand, + func(r *rand.Rand) { threshold = GenTallyParamsThreshold(r) }, + ) + + var veto sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, TallyParamsVeto, &veto, simState.Rand, + func(r *rand.Rand) { veto = GenTallyParamsVeto(r) }, + ) + + govGenesis := types.NewGenesisState( + startingProposalID, + types.NewDepositParams(minDeposit, depositPeriod), + types.NewVotingParams(votingPeriod), + types.NewTallyParams(quorum, threshold, veto), + ) + + fmt.Printf("Selected randomly generated governance parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, govGenesis)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(govGenesis) +} diff --git a/x/gov/simulation/operations/msgs.go b/x/gov/simulation/operations/msgs.go index f7dcba519de0..35010fef93d9 100644 --- a/x/gov/simulation/operations/msgs.go +++ b/x/gov/simulation/operations/msgs.go @@ -14,7 +14,7 @@ import ( // ContentSimulator defines a function type alias for generating random proposal // content. -type ContentSimulator func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) gov.Content +type ContentSimulator func(r *rand.Rand, ctx sdk.Context, accs []simulation.Account) gov.Content // SimulateSubmittingVotingAndSlashingForProposal simulates creating a msg Submit Proposal // voting on the proposal, and subsequently slashing the proposal. It is implemented using @@ -51,7 +51,7 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, contentSim Con // 1) submit proposal now sender := simulation.RandomAcc(r, accs) - content := contentSim(r, app, ctx, accs) + content := contentSim(r, ctx, accs) msg, err := simulationCreateMsgSubmitProposal(r, content, sender) if err != nil { return simulation.NoOpMsg(gov.ModuleName), nil, err @@ -107,7 +107,7 @@ func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, handler sdk.Hand } // SimulateTextProposalContent returns random text proposal content. -func SimulateTextProposalContent(r *rand.Rand, _ *baseapp.BaseApp, _ sdk.Context, _ []simulation.Account) gov.Content { +func SimulateTextProposalContent(r *rand.Rand, _ sdk.Context, _ []simulation.Account) gov.Content { return gov.NewTextProposal( simulation.RandStringOfLength(r, 140), simulation.RandStringOfLength(r, 5000), diff --git a/x/gov/simulation/params.go b/x/gov/simulation/params.go new file mode 100644 index 000000000000..c43f3952e15f --- /dev/null +++ b/x/gov/simulation/params.go @@ -0,0 +1,68 @@ +package simulation + +// DONTCOVER + +import ( + "encoding/json" + "fmt" + "math/rand" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +const ( + keyVotingParams = "votingparams" + keyDepositParams = "depositparams" + keyTallyParams = "tallyparams" + subkeyQuorum = "quorum" + subkeyThreshold = "threshold" + subkeyVeto = "veto" +) + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation +func ParamChanges(r *rand.Rand) []simulation.ParamChange { + return []simulation.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, keyVotingParams, "", + func(r *rand.Rand) string { + return fmt.Sprintf(`{"voting_period": "%d"}`, GenVotingParamsVotingPeriod(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyDepositParams, "", + func(r *rand.Rand) string { + return fmt.Sprintf(`{"max_deposit_period": "%d"}`, GenDepositParamsDepositPeriod(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyTallyParams, "", + func(r *rand.Rand) string { + changes := []struct { + key string + value sdk.Dec + }{ + {subkeyQuorum, GenTallyParamsQuorum(r)}, + {subkeyThreshold, GenTallyParamsThreshold(r)}, + {subkeyVeto, GenTallyParamsVeto(r)}, + } + + pc := make(map[string]string) + numChanges := simulation.RandIntBetween(r, 1, len(changes)) + for i := 0; i < numChanges; i++ { + c := changes[r.Intn(len(changes))] + + _, ok := pc[c.key] + for ok { + c := changes[r.Intn(len(changes))] + _, ok = pc[c.key] + } + + pc[c.key] = c.value.String() + } + + bz, _ := json.Marshal(pc) + return string(bz) + }, + ), + } +} diff --git a/x/mint/alias.go b/x/mint/alias.go index e3a9bcf1e9bb..4d31e826bcf0 100644 --- a/x/mint/alias.go +++ b/x/mint/alias.go @@ -24,6 +24,9 @@ var ( // functions aliases NewKeeper = keeper.NewKeeper NewQuerier = keeper.NewQuerier + NewGenesisState = types.NewGenesisState + DefaultGenesisState = types.DefaultGenesisState + ValidateGenesis = types.ValidateGenesis NewMinter = types.NewMinter InitialMinter = types.InitialMinter DefaultInitialMinter = types.DefaultInitialMinter @@ -45,7 +48,8 @@ var ( ) type ( - Keeper = keeper.Keeper - Minter = types.Minter - Params = types.Params + Keeper = keeper.Keeper + GenesisState = types.GenesisState + Minter = types.Minter + Params = types.Params ) diff --git a/x/mint/genesis.go b/x/mint/genesis.go index 68722158e66f..331852b43550 100644 --- a/x/mint/genesis.go +++ b/x/mint/genesis.go @@ -4,28 +4,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// GenesisState - minter state -type GenesisState struct { - Minter Minter `json:"minter" yaml:"minter"` // minter object - Params Params `json:"params" yaml:"params"` // inflation params -} - -// NewGenesisState creates a new GenesisState object -func NewGenesisState(minter Minter, params Params) GenesisState { - return GenesisState{ - Minter: minter, - Params: params, - } -} - -// DefaultGenesisState creates a default GenesisState object -func DefaultGenesisState() GenesisState { - return GenesisState{ - Minter: DefaultInitialMinter(), - Params: DefaultParams(), - } -} - // InitGenesis new mint genesis func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { keeper.SetMinter(ctx, data.Minter) @@ -38,19 +16,3 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { params := keeper.GetParams(ctx) return NewGenesisState(minter, params) } - -// ValidateGenesis validates the provided genesis state to ensure the -// expected invariants holds. -func ValidateGenesis(data GenesisState) error { - err := ValidateParams(data.Params) - if err != nil { - return err - } - - err = ValidateMinter(data.Minter) - if err != nil { - return err - } - - return nil -} diff --git a/x/mint/internal/types/genesis.go b/x/mint/internal/types/genesis.go new file mode 100644 index 000000000000..43ee15f1e0d6 --- /dev/null +++ b/x/mint/internal/types/genesis.go @@ -0,0 +1,34 @@ +package types + +// GenesisState - minter state +type GenesisState struct { + Minter Minter `json:"minter" yaml:"minter"` // minter object + Params Params `json:"params" yaml:"params"` // inflation params +} + +// NewGenesisState creates a new GenesisState object +func NewGenesisState(minter Minter, params Params) GenesisState { + return GenesisState{ + Minter: minter, + Params: params, + } +} + +// DefaultGenesisState creates a default GenesisState object +func DefaultGenesisState() GenesisState { + return GenesisState{ + Minter: DefaultInitialMinter(), + Params: DefaultParams(), + } +} + +// ValidateGenesis validates the provided genesis state to ensure the +// expected invariants holds. +func ValidateGenesis(data GenesisState) error { + err := ValidateParams(data.Params) + if err != nil { + return err + } + + return ValidateMinter(data.Minter) +} diff --git a/x/mint/module.go b/x/mint/module.go index 33b93ab54e81..115150dc9eaa 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -2,6 +2,7 @@ package mint import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -15,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/mint/client/cli" "github.com/cosmos/cosmos-sdk/x/mint/client/rest" "github.com/cosmos/cosmos-sdk/x/mint/simulation" + sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -75,6 +77,16 @@ func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[StoreKey] = simulation.DecodeStore } +// GenerateGenesisState creates a randomized GenState of the mint module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams creates randomized mint param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + //____________________________________________________________________________ // AppModule implements an application module for the mint module. diff --git a/x/mint/simulation/genesis.go b/x/mint/simulation/genesis.go new file mode 100644 index 000000000000..07a750d30e6d --- /dev/null +++ b/x/mint/simulation/genesis.go @@ -0,0 +1,92 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/codec" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/mint/internal/types" +) + +// Simulation parameter constants +const ( + Inflation = "inflation" + InflationRateChange = "inflation_rate_change" + InflationMax = "inflation_max" + InflationMin = "inflation_min" + GoalBonded = "goal_bonded" +) + +// GenInflation randomized Inflation +func GenInflation(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(int64(r.Intn(99)), 2) +} + +// GenInflationRateChange randomized InflationRateChange +func GenInflationRateChange(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(int64(r.Intn(99)), 2) +} + +// GenInflationMax randomized InflationMax +func GenInflationMax(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(20, 2) +} + +// GenInflationMin randomized InflationMin +func GenInflationMin(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(7, 2) +} + +// GenGoalBonded randomized GoalBonded +func GenGoalBonded(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(67, 2) +} + +// RandomizedGenState generates a random GenesisState for mint +func RandomizedGenState(simState *module.SimulationState) { + // minter + var inflation sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, Inflation, &inflation, simState.Rand, + func(r *rand.Rand) { inflation = GenInflation(r) }, + ) + + // params + var inflationRateChange sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, InflationRateChange, &inflationRateChange, simState.Rand, + func(r *rand.Rand) { inflationRateChange = GenInflationRateChange(r) }, + ) + + var inflationMax sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, InflationMax, &inflationMax, simState.Rand, + func(r *rand.Rand) { inflationMax = GenInflationMax(r) }, + ) + + var inflationMin sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, InflationMin, &inflationMin, simState.Rand, + func(r *rand.Rand) { inflationMin = GenInflationMin(r) }, + ) + + var goalBonded sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, GoalBonded, &goalBonded, simState.Rand, + func(r *rand.Rand) { goalBonded = GenGoalBonded(r) }, + ) + + mintDenom := sdk.DefaultBondDenom + blocksPerYear := uint64(60 * 60 * 8766 / 5) + params := types.NewParams(mintDenom, inflationRateChange, inflationMax, inflationMin, goalBonded, blocksPerYear) + + mintGenesis := types.NewGenesisState(types.InitialMinter(inflation), params) + + fmt.Printf("Selected randomly generated minting parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, mintGenesis)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(mintGenesis) +} diff --git a/x/mint/simulation/params.go b/x/mint/simulation/params.go new file mode 100644 index 000000000000..506b3bf45dc5 --- /dev/null +++ b/x/mint/simulation/params.go @@ -0,0 +1,45 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/x/mint/internal/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +const ( + keyInflationRateChange = "InflationRateChange" + keyInflationMax = "InflationMax" + keyInflationMin = "InflationMin" + keyGoalBonded = "GoalBonded" +) + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation +func ParamChanges(r *rand.Rand) []simulation.ParamChange { + return []simulation.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, keyInflationRateChange, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenInflationRateChange(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyInflationMax, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenInflationMax(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyInflationMin, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenInflationMin(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyGoalBonded, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenGoalBonded(r)) + }, + ), + } +} diff --git a/x/nft/integration_test.go b/x/nft/integration_test.go index 3a893004fc5a..3b8264ee56c6 100644 --- a/x/nft/integration_test.go +++ b/x/nft/integration_test.go @@ -13,7 +13,7 @@ import ( // nolint: deadcode unused var ( - denom1 = "test-denom" + denom1 = "test-denom" denom2 = "test-denom2" denom3 = "test-denom3" id = "1" @@ -22,7 +22,7 @@ var ( address = types.CreateTestAddrs(1)[0] address2 = types.CreateTestAddrs(2)[1] address3 = types.CreateTestAddrs(3)[2] - tokenURI1 = "https://google.com/token-1.json" + tokenURI1 = "https://google.com/token-1.json" tokenURI2 = "https://google.com/token-2.json" ) diff --git a/x/nft/module.go b/x/nft/module.go index 9532eac7015a..f6cdcf2ee980 100644 --- a/x/nft/module.go +++ b/x/nft/module.go @@ -4,6 +4,7 @@ package nft import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -17,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/nft/client/cli" "github.com/cosmos/cosmos-sdk/x/nft/client/rest" "github.com/cosmos/cosmos-sdk/x/nft/simulation" + sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -76,11 +78,21 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { // AppModuleSimulation defines the module simulation functions used by the gov module. type AppModuleSimulation struct{} -// RegisterStoreDecoder performs a no-op. +// RegisterStoreDecoder registers a decoder for nft module's types func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[StoreKey] = simulation.DecodeStore } +// GenerateGenesisState creates a randomized GenState of the nft module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams doesn't create randomized nft param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(_ *rand.Rand) []sim.ParamChange { + return nil +} + //____________________________________________________________________________ // AppModule supply app module diff --git a/x/nft/simulation/genesis.go b/x/nft/simulation/genesis.go index 47a0c59bc74c..216ebc963386 100644 --- a/x/nft/simulation/genesis.go +++ b/x/nft/simulation/genesis.go @@ -1,48 +1,53 @@ package simulation import ( - "encoding/json" "fmt" - "math/rand" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/nft/internal/types" "github.com/cosmos/cosmos-sdk/x/simulation" ) -// GenNFTGenesisState generates a random GenesisState for nft -func GenNFTGenesisState(cdc *codec.Codec, r *rand.Rand, accs []simulation.Account, ap simulation.AppParams, genesisState map[string]json.RawMessage) { - const ( - Kitties = "crypto-kitties" - Doggos = "crypto-doggos" - ) +const ( + kities = "crypto-kities" + doggos = "crypto-doggos" +) - collections := types.NewCollections(types.NewCollection(Kitties, types.NFTs{}), types.NewCollection(Doggos, types.NFTs{})) +// RandomizedGenState generates a random GenesisState for nft +func RandomizedGenState(simState *module.SimulationState) { + collections := types.NewCollections(types.NewCollection(kities, types.NFTs{}), types.NewCollection(doggos, types.NFTs{})) var ownerships []types.Owner - for _, acc := range accs { - if r.Intn(100) < 50 { + for _, acc := range simState.Accounts { + // 10% of accounts own an NFT + if simState.Rand.Intn(100) < 10 { baseNFT := types.NewBaseNFT( - simulation.RandStringOfLength(r, 10), // id + simulation.RandStringOfLength(simState.Rand, 10), // id acc.Address, - simulation.RandStringOfLength(r, 45), // tokenURI + simulation.RandStringOfLength(simState.Rand, 45), // tokenURI + ) + + var ( + idCollection types.IDCollection + err error ) - var idCollection types.IDCollection - var err error - if r.Intn(100) < 50 { + // 50% doggos and 50% kitties + if simState.Rand.Intn(100) < 50 { collections[0], err = collections[0].AddNFT(&baseNFT) if err != nil { panic(err) } - idCollection = types.NewIDCollection(Kitties, []string{baseNFT.ID}) + idCollection = types.NewIDCollection(kities, []string{baseNFT.ID}) } else { collections[1], err = collections[1].AddNFT(&baseNFT) if err != nil { panic(err) } - idCollection = types.NewIDCollection(Doggos, []string{baseNFT.ID}) + idCollection = types.NewIDCollection(doggos, []string{baseNFT.ID}) } + ownership := types.NewOwner(acc.Address, idCollection) ownerships = append(ownerships, ownership) } @@ -50,6 +55,6 @@ func GenNFTGenesisState(cdc *codec.Codec, r *rand.Rand, accs []simulation.Accoun nftGenesis := types.NewGenesisState(ownerships, collections) - fmt.Printf("Selected randomly generated NFT parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, nftGenesis)) - genesisState[types.ModuleName] = cdc.MustMarshalJSON(nftGenesis) + fmt.Printf("Selected randomly generated NFT genesis state:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, nftGenesis)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(nftGenesis) } diff --git a/x/params/simulation/operations/msgs.go b/x/params/simulation/operations/msgs.go index b6b869dc6f23..92c9b253ede0 100644 --- a/x/params/simulation/operations/msgs.go +++ b/x/params/simulation/operations/msgs.go @@ -1,185 +1,53 @@ package operations import ( - "encoding/json" - "fmt" "math/rand" - "time" - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" + govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/simulation" ) -type simParamChange struct { - subspace string - key string - subkey string - simValue func(r *rand.Rand) string -} +// SimulateParamChangeProposalContent returns random parameter change content. +// It will generate a ParameterChangeProposal object with anywhere between 1 and +// the total amount of defined parameters changes, all of which have random valid values. +func SimulateParamChangeProposalContent(paramChangePool []simulation.ParamChange) govsimops.ContentSimulator { + return func(r *rand.Rand, _ sdk.Context, _ []simulation.Account) govtypes.Content { -func (spc simParamChange) compKey() string { - return fmt.Sprintf("%s/%s/%s", spc.subkey, spc.key, spc.subkey) -} + lenParamChange := len(paramChangePool) + if lenParamChange == 0 { + panic("param changes array is empty") + } -// paramChangePool defines a static slice of possible simulated parameter changes -// where each simParamChange corresponds to a ParamChange with a simValue -// function to generate a simulated new value. -var paramChangePool = []simParamChange{ - // staking parameters - { - "staking", - "MaxValidators", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("%d", simulation.ModuleParamSimulator[simulation.MaxValidators](r).(uint16)) - }, - }, - { - "staking", - "UnbondingTime", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.UnbondingTime](r).(time.Duration)) - }, - }, - // slashing parameters - { - "slashing", - "SignedBlocksWindow", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.SignedBlocksWindow](r).(int64)) - }, - }, - { - "slashing", - "MinSignedPerWindow", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator[simulation.MinSignedPerWindow](r).(sdk.Dec)) - }, - }, - { - "slashing", - "SlashFractionDowntime", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator[simulation.SlashFractionDowntime](r).(sdk.Dec)) - }, - }, - // minting parameters - { - "mint", - "InflationRateChange", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator[simulation.InflationRateChange](r).(sdk.Dec)) - }, - }, - // gov parameters - { - "gov", - "votingparams", - "", - func(r *rand.Rand) string { - return fmt.Sprintf(`{"voting_period": "%d"}`, simulation.ModuleParamSimulator[simulation.VotingParamsVotingPeriod](r).(time.Duration)) - }, - }, - { - "gov", - "depositparams", - "", - func(r *rand.Rand) string { - return fmt.Sprintf(`{"max_deposit_period": "%d"}`, simulation.ModuleParamSimulator[simulation.VotingParamsVotingPeriod](r).(time.Duration)) - }, - }, - { - "gov", - "tallyparams", - "", - func(r *rand.Rand) string { - changes := []struct { - key string - value 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)}, - } + numChanges := simulation.RandIntBetween(r, 1, lenParamChange) + paramChanges := make([]params.ParamChange, numChanges, numChanges) - pc := make(map[string]string) - numChanges := simulation.RandIntBetween(r, 1, len(changes)) - for i := 0; i < numChanges; i++ { - c := changes[r.Intn(len(changes))] + // map from key to empty struct; used only for look-up of the keys of the + // parameters that are already in the random set of changes. + paramChangesKeys := make(map[string]struct{}) - _, ok := pc[c.key] - for ok { - c := changes[r.Intn(len(changes))] - _, ok = pc[c.key] - } + for i := 0; i < numChanges; i++ { + spc := paramChangePool[r.Intn(len(paramChangePool))] - pc[c.key] = c.value.String() + // do not include duplicate parameter changes for a given subspace/key + _, ok := paramChangesKeys[spc.ComposedKey()] + for ok { + spc = paramChangePool[r.Intn(len(paramChangePool))] + _, ok = paramChangesKeys[spc.ComposedKey()] } - bz, _ := json.Marshal(pc) - return string(bz) - }, - }, - // auth parameters - { - "auth", - "MaxMemoCharacters", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.MaxMemoChars](r).(uint64)) - }, - }, - { - "auth", - "TxSigLimit", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.TxSigLimit](r).(uint64)) - }, - }, - { - "auth", - "TxSizeCostPerByte", - "", - func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", simulation.ModuleParamSimulator[simulation.TxSizeCostPerByte](r).(uint64)) - }, - }, -} - -// SimulateParamChangeProposalContent returns random parameter change content. -// It will generate a ParameterChangeProposal object with anywhere between 1 and -// 3 parameter changes all of which have random, but valid values. -func SimulateParamChangeProposalContent(r *rand.Rand, _ *baseapp.BaseApp, _ sdk.Context, _ []simulation.Account) govtypes.Content { - numChanges := simulation.RandIntBetween(r, 1, len(paramChangePool)/2) - paramChanges := make([]params.ParamChange, numChanges, numChanges) - paramChangesKeys := make(map[string]struct{}) - - for i := 0; i < numChanges; i++ { - spc := paramChangePool[r.Intn(len(paramChangePool))] - - // do not include duplicate parameter changes for a given subspace/key - _, ok := paramChangesKeys[spc.compKey()] - for ok { - spc = paramChangePool[r.Intn(len(paramChangePool))] - _, ok = paramChangesKeys[spc.compKey()] + // add a new distinct parameter to the set of changes and register the key + // to avoid further duplicates + paramChangesKeys[spc.ComposedKey()] = struct{}{} + paramChanges[i] = params.NewParamChangeWithSubkey(spc.Subspace, spc.Key, spc.Subkey, spc.SimValue(r)) } - paramChangesKeys[spc.compKey()] = struct{}{} - paramChanges[i] = params.NewParamChangeWithSubkey(spc.subspace, spc.key, spc.subkey, spc.simValue(r)) + return params.NewParameterChangeProposal( + simulation.RandStringOfLength(r, 140), // title + simulation.RandStringOfLength(r, 5000), // description + paramChanges, // set of changes + ) } - - return params.NewParameterChangeProposal( - simulation.RandStringOfLength(r, 140), - simulation.RandStringOfLength(r, 5000), - paramChanges, - ) } diff --git a/x/params/subspace/subspace.go b/x/params/subspace/subspace.go index 457c0133345d..bd328b10407a 100644 --- a/x/params/subspace/subspace.go +++ b/x/params/subspace/subspace.go @@ -1,7 +1,7 @@ package subspace import ( - "errors" + "fmt" "reflect" "github.com/cosmos/cosmos-sdk/codec" @@ -145,7 +145,7 @@ func (s Subspace) Modified(ctx sdk.Context, key []byte) bool { func (s Subspace) checkType(store sdk.KVStore, key []byte, param interface{}) { attr, ok := s.table.m[string(key)] if !ok { - panic("Parameter not registered") + panic(fmt.Sprintf("parameter %s not registered", string(key))) } ty := attr.ty @@ -183,7 +183,7 @@ func (s Subspace) Set(ctx sdk.Context, key []byte, param interface{}) { func (s Subspace) Update(ctx sdk.Context, key []byte, param []byte) error { attr, ok := s.table.m[string(key)] if !ok { - panic("Parameter not registered") + panic(fmt.Sprintf("parameter %s not registered", string(key))) } ty := attr.ty @@ -227,7 +227,7 @@ func (s Subspace) UpdateWithSubkey(ctx sdk.Context, key []byte, subkey []byte, p attr, ok := s.table.m[string(concatkey)] if !ok { - return errors.New("parameter not registered") + return fmt.Errorf("parameter %s not registered", string(key)) } ty := attr.ty diff --git a/x/simulation/doc.go b/x/simulation/doc.go index 42fc97416e68..6543bc5c1532 100644 --- a/x/simulation/doc.go +++ b/x/simulation/doc.go @@ -1,25 +1,121 @@ /* -Package simulation implements a simulation framework for any state machine -built on the SDK which utilizes auth. - -It is primarily intended for fuzz testing the integration of modules. It will -test that the provided operations are interoperable, and that the desired -invariants hold. It can additionally be used to detect what the performance -benchmarks in the system are, by using benchmarking mode and cpu / mem -profiling. If it detects a failure, it provides the entire log of what was ran. - -The simulator takes as input: a random seed, the set of operations to run, the -invariants to test, and additional parameters to configure how long to run, and -misc. parameters that affect simulation speed. - -It is intended that every module provides a list of Operations which will -randomly create and run a message / tx in a manner that is interesting to fuzz, -and verify that the state transition was executed as exported. Each module -should additionally provide methods to assert that the desired invariants hold. - -Then to perform a randomized simulation, select the set of desired operations, -the weightings for each, the invariants you want to test, and how long to run -it for. Then run simulation.Simulate! The simulator will handle things like -ensuring that validators periodically double signing, or go offline. +Package simulation 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 \ + -Enabled=true \ + -NumBlocks=100 \ + -BlockSize=200 \ + -Commit=true \ + -Seed=99 \ + -Period=5 \ + -v -timeout 24h + +To execute simulation from a genesis file: + + $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ + -run=TestFullAppSimulation \ + -Enabled=true \ + -NumBlocks=100 \ + -BlockSize=200 \ + -Commit=true \ + -Seed=99 \ + -Period=5 \ + -Genesis=/path/to/genesis.json \ + -v -timeout 24h + +To execute simulation from a simulation params file: + + $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ + -run=TestFullAppSimulation \ + -Enabled=true \ + -NumBlocks=100 \ + -BlockSize=200 \ + -Commit=true \ + -Seed=99 \ + -Period=5 \ + -Params=/path/to/params.json \ + -v -timeout 24h + +To export the simulation params to a file at a given block height: + + $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ + -run=TestFullAppSimulation \ + -Enabled=true \ + -NumBlocks=100 \ + -BlockSize=200 \ + -Commit=true \ + -Seed=99 \ + -Period=5 \ + -ExportParamsPath=/path/to/params.json \ + -ExportParamsHeight=50 \ + -v -timeout 24h + + +To export the simulation app state (i.e genesis) to a file: + + $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ + -run=TestFullAppSimulation \ + -Enabled=true \ + -NumBlocks=100 \ + -BlockSize=200 \ + -Commit=true \ + -Seed=99 \ + -Period=5 \ + -ExportStatePath=/path/to/genesis.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 simulation diff --git a/x/simulation/params.go b/x/simulation/params.go index 526f1972f694..63aab6c40210 100644 --- a/x/simulation/params.go +++ b/x/simulation/params.go @@ -2,51 +2,21 @@ package simulation import ( "encoding/json" + "fmt" "math/rand" - "time" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" ) -// nolint const ( // Minimum time per block minTimePerBlock int64 = 10000 / 2 // 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 +// TODO: explain transitional matrix usage var ( // Currently there are 3 different liveness types, // fully online, spotty connection, offline. @@ -63,97 +33,15 @@ var ( {15, 92, 1}, {0, 3, 99}, }) - - // ModuleParamSimulator defines module parameter value simulators. All - // values simulated should be within valid acceptable range for the given - // parameter. - ModuleParamSimulator = map[string]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{} { - return uint64(r.Intn(7) + 1) - }, - TxSizeCostPerByte: func(r *rand.Rand) interface{} { - return uint64(RandIntBetween(r, 5, 15)) - }, - SigVerifyCostED25519: func(r *rand.Rand) interface{} { - return uint64(RandIntBetween(r, 500, 1000)) - }, - SigVerifyCostSECP256K1: func(r *rand.Rand) interface{} { - return uint64(RandIntBetween(r, 500, 1000)) - }, - DepositParamsMinDeposit: func(r *rand.Rand) interface{} { - return sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, int64(RandIntBetween(r, 1, 1e3)))} - }, - VotingParamsVotingPeriod: func(r *rand.Rand) interface{} { - return time.Duration(RandIntBetween(r, 1, 2*60*60*24*2)) * time.Second - }, - TallyParamsQuorum: func(r *rand.Rand) interface{} { - return sdk.NewDecWithPrec(int64(RandIntBetween(r, 334, 500)), 3) - }, - TallyParamsThreshold: func(r *rand.Rand) interface{} { - return sdk.NewDecWithPrec(int64(RandIntBetween(r, 450, 550)), 3) - }, - TallyParamsVeto: func(r *rand.Rand) interface{} { - return sdk.NewDecWithPrec(int64(RandIntBetween(r, 250, 334)), 3) - }, - 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{} { - return uint16(r.Intn(250) + 1) - }, - SignedBlocksWindow: func(r *rand.Rand) interface{} { - return int64(RandIntBetween(r, 10, 1000)) - }, - MinSignedPerWindow: func(r *rand.Rand) interface{} { - return sdk.NewDecWithPrec(int64(r.Intn(10)), 1) - }, - DowntimeJailDuration: func(r *rand.Rand) interface{} { - return time.Duration(RandIntBetween(r, 60, 60*60*24)) * time.Second - }, - SlashFractionDoubleSign: func(r *rand.Rand) interface{} { - return sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))) - }, - SlashFractionDowntime: func(r *rand.Rand) interface{} { - return sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))) - }, - InflationRateChange: func(r *rand.Rand) interface{} { - return sdk.NewDecWithPrec(int64(r.Intn(99)), 2) - }, - 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{} { - return sdk.NewDecWithPrec(7, 2) - }, - 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)) - }, - } ) -// TODO add description -type ( - AppParams map[string]json.RawMessage - ParamSimulator func(r *rand.Rand) -) +// AppParams defines a flat JSON of key/values for all possible configurable +// simulation parameters. It might contain: operation weights, simulation parameters +// and flattened module state parameters (i.e not stored under it's respective module name). +type AppParams map[string]json.RawMessage + +// ParamSimulator creates a parameter value from a source of random number +type 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 @@ -167,7 +55,7 @@ func (sp AppParams) GetOrGenerate(cdc *codec.Codec, key string, ptr interface{}, ps(r) } -// Simulation parameters +// Params define the parameters necessary for running the simulations type Params struct { PastEvidenceFraction float64 NumKeys int @@ -177,7 +65,7 @@ type Params struct { BlockSizeTransitionMatrix TransitionMatrix } -// Return random simulation parameters +// RandomParams for simulation func RandomParams(r *rand.Rand) Params { return Params{ PastEvidenceFraction: r.Float64(), @@ -188,3 +76,32 @@ func RandomParams(r *rand.Rand) Params { BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix, } } + +//----------------------------------------------------------------------------- +// Param change proposals + +// SimValFn function to generate the randomized parameter change value +type SimValFn func(r *rand.Rand) string + +// ParamChange defines the object used for simulating parameter change proposals +type ParamChange struct { + Subspace string + Key string + Subkey string + SimValue SimValFn +} + +// NewSimParamChange creates a new ParamChange instance +func NewSimParamChange(subspace, key, subkey string, simVal SimValFn) ParamChange { + return ParamChange{ + Subspace: subspace, + Key: key, + Subkey: subkey, + SimValue: simVal, + } +} + +// ComposedKey creates a new composed key for the param change proposal +func (spc ParamChange) ComposedKey() string { + return fmt.Sprintf("%s/%s/%s", spc.Subspace, spc.Key, spc.Subkey) +} diff --git a/x/simulation/rand_util.go b/x/simulation/rand_util.go index ce2a8c4c1a8e..fc73619c1682 100644 --- a/x/simulation/rand_util.go +++ b/x/simulation/rand_util.go @@ -18,7 +18,8 @@ const ( // shamelessly copied from // https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang#31832326 -// Generate a random string of a particular length + +// RandStringOfLength generates a random string of a particular length func RandStringOfLength(r *rand.Rand, n int) string { b := make([]byte, n) // A src.Int63() generates 63 random bits, enough for letterIdxMax characters! @@ -36,7 +37,7 @@ func RandStringOfLength(r *rand.Rand, n int) string { return string(b) } -// get a rand positive sdk.Int +// RandPositiveInt get a rand positive sdk.Int func RandPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) { if !max.GT(sdk.OneInt()) { return sdk.Int{}, errors.New("max too small") @@ -45,7 +46,7 @@ func RandPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) { return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil } -// Generate a random amount +// RandomAmount generates a random amount // Note: The range of RandomAmount includes max, and is, in fact, biased to return max as well as 0. func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { var randInt = big.NewInt(0) @@ -87,9 +88,10 @@ func RandIntBetween(r *rand.Rand, min, max int) int { return r.Intn(max-min) + min } -// Derive a new rand deterministically from a rand. +// DeriveRand derives a new Rand deterministically from another random source. // Unlike rand.New(rand.NewSource(seed)), the result is "more random" // depending on the source and state of r. +// // NOTE: not crypto safe. func DeriveRand(r *rand.Rand) *rand.Rand { const num = 8 // TODO what's a good number? Too large is too slow. diff --git a/x/slashing/internal/types/params.go b/x/slashing/internal/types/params.go index f66241d4de06..20892dd8224f 100644 --- a/x/slashing/internal/types/params.go +++ b/x/slashing/internal/types/params.go @@ -52,7 +52,7 @@ type Params struct { // NewParams creates a new Params object func NewParams(maxEvidenceAge time.Duration, signedBlocksWindow int64, minSignedPerWindow sdk.Dec, downtimeJailDuration time.Duration, - slashFractionDoubleSign sdk.Dec, slashFractionDowntime sdk.Dec) Params { + slashFractionDoubleSign, slashFractionDowntime sdk.Dec) Params { return Params{ MaxEvidenceAge: maxEvidenceAge, diff --git a/x/slashing/module.go b/x/slashing/module.go index 986915a11531..9a179b67d931 100644 --- a/x/slashing/module.go +++ b/x/slashing/module.go @@ -2,6 +2,7 @@ package slashing import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -12,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + sim "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" @@ -80,6 +82,16 @@ func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[StoreKey] = simulation.DecodeStore } +// GenerateGenesisState creates a randomized GenState of the slashing module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams creates randomized slashing param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + //____________________________________________________________________________ // AppModule implements an application module for the slashing module. diff --git a/x/slashing/simulation/genesis.go b/x/slashing/simulation/genesis.go new file mode 100644 index 000000000000..dfe9e813fe16 --- /dev/null +++ b/x/slashing/simulation/genesis.go @@ -0,0 +1,93 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" +) + +// Simulation parameter constants +const ( + SignedBlocksWindow = "signed_blocks_window" + MinSignedPerWindow = "min_signed_per_window" + DowntimeJailDuration = "downtime_jail_duration" + SlashFractionDoubleSign = "slash_fraction_double_sign" + SlashFractionDowntime = "slash_fraction_downtime" +) + +// GenSignedBlocksWindow randomized SignedBlocksWindow +func GenSignedBlocksWindow(r *rand.Rand) int64 { + return int64(simulation.RandIntBetween(r, 10, 1000)) +} + +// GenMinSignedPerWindow randomized MinSignedPerWindow +func GenMinSignedPerWindow(r *rand.Rand) sdk.Dec { + return sdk.NewDecWithPrec(int64(r.Intn(10)), 1) +} + +// GenDowntimeJailDuration randomized DowntimeJailDuration +func GenDowntimeJailDuration(r *rand.Rand) time.Duration { + return time.Duration(simulation.RandIntBetween(r, 60, 60*60*24)) * time.Second +} + +// GenSlashFractionDoubleSign randomized SlashFractionDoubleSign +func GenSlashFractionDoubleSign(r *rand.Rand) sdk.Dec { + return sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))) +} + +// GenSlashFractionDowntime randomized SlashFractionDowntime +func GenSlashFractionDowntime(r *rand.Rand) sdk.Dec { + return sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))) +} + +// RandomizedGenState generates a random GenesisState for slashing +func RandomizedGenState(simState *module.SimulationState) { + var signedBlocksWindow int64 + simState.AppParams.GetOrGenerate( + simState.Cdc, SignedBlocksWindow, &signedBlocksWindow, simState.Rand, + func(r *rand.Rand) { signedBlocksWindow = GenSignedBlocksWindow(r) }, + ) + + var minSignedPerWindow sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, MinSignedPerWindow, &minSignedPerWindow, simState.Rand, + func(r *rand.Rand) { minSignedPerWindow = GenMinSignedPerWindow(r) }, + ) + + var downtimeJailDuration time.Duration + simState.AppParams.GetOrGenerate( + simState.Cdc, DowntimeJailDuration, &downtimeJailDuration, simState.Rand, + func(r *rand.Rand) { downtimeJailDuration = GenDowntimeJailDuration(r) }, + ) + + var slashFractionDoubleSign sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, SlashFractionDoubleSign, &slashFractionDoubleSign, simState.Rand, + func(r *rand.Rand) { slashFractionDoubleSign = GenSlashFractionDoubleSign(r) }, + ) + + var slashFractionDowntime sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, SlashFractionDowntime, &slashFractionDowntime, simState.Rand, + func(r *rand.Rand) { slashFractionDowntime = GenSlashFractionDowntime(r) }, + ) + + params := types.NewParams( + simState.UnbondTime, signedBlocksWindow, minSignedPerWindow, + downtimeJailDuration, slashFractionDoubleSign, slashFractionDowntime, + ) + + slashingGenesis := types.NewGenesisState(params, nil, nil) + + fmt.Printf("Selected randomly generated slashing parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, slashingGenesis.Params)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(slashingGenesis) +} diff --git a/x/slashing/simulation/params.go b/x/slashing/simulation/params.go new file mode 100644 index 000000000000..447073912c1a --- /dev/null +++ b/x/slashing/simulation/params.go @@ -0,0 +1,39 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" +) + +const ( + keySignedBlocksWindow = "SignedBlocksWindow" + keyMinSignedPerWindow = "MinSignedPerWindow" + keySlashFractionDowntime = "SlashFractionDowntime" +) + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation +func ParamChanges(r *rand.Rand) []simulation.ParamChange { + return []simulation.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, keySignedBlocksWindow, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%d\"", GenSignedBlocksWindow(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyMinSignedPerWindow, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenMinSignedPerWindow(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keySlashFractionDowntime, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenSlashFractionDowntime(r)) + }, + ), + } +} diff --git a/x/staking/module.go b/x/staking/module.go index 6b42d4b9a2c0..75833037d76b 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -2,6 +2,7 @@ package staking import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -16,6 +17,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + sim "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/cosmos/cosmos-sdk/x/staking/client/cli" "github.com/cosmos/cosmos-sdk/x/staking/client/rest" "github.com/cosmos/cosmos-sdk/x/staking/simulation" @@ -105,6 +107,16 @@ func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[StoreKey] = simulation.DecodeStore } +// GenerateGenesisState creates a randomized GenState of the staking module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams creates randomized staking param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + //____________________________________________________________________________ // AppModule implements an application module for the staking module. diff --git a/x/staking/simulation/genesis.go b/x/staking/simulation/genesis.go new file mode 100644 index 000000000000..a73ac7eb750c --- /dev/null +++ b/x/staking/simulation/genesis.go @@ -0,0 +1,78 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Simulation parameter constants +const ( + UnbondingTime = "unbonding_time" + MaxValidators = "max_validators" +) + +// GenUnbondingTime randomized UnbondingTime +func GenUnbondingTime(r *rand.Rand) (ubdTime time.Duration) { + return time.Duration(simulation.RandIntBetween(r, 60, 60*60*24*3*2)) * time.Second +} + +// GenMaxValidators randomized MaxValidators +func GenMaxValidators(r *rand.Rand) (maxValidators uint16) { + return uint16(r.Intn(250) + 1) +} + +// RandomizedGenState generates a random GenesisState for staking +func RandomizedGenState(simState *module.SimulationState) { + // params + var unbondTime time.Duration + simState.AppParams.GetOrGenerate( + simState.Cdc, UnbondingTime, &unbondTime, simState.Rand, + func(r *rand.Rand) { unbondTime = GenUnbondingTime(r) }, + ) + + var maxValidators uint16 + simState.AppParams.GetOrGenerate( + simState.Cdc, MaxValidators, &maxValidators, simState.Rand, + func(r *rand.Rand) { maxValidators = GenMaxValidators(r) }, + ) + + // NOTE: the slashing module need to be defined after the staking module on the + // NewSimulationManager constructor for this to work + simState.UnbondTime = unbondTime + + params := types.NewParams(simState.UnbondTime, maxValidators, 7, sdk.DefaultBondDenom) + + // validators & delegations + var ( + validators []types.Validator + delegations []types.Delegation + ) + + valAddrs := make([]sdk.ValAddress, simState.NumBonded) + for i := 0; i < int(simState.NumBonded); i++ { + valAddr := sdk.ValAddress(simState.Accounts[i].Address) + valAddrs[i] = valAddr + + validator := types.NewValidator(valAddr, simState.Accounts[i].PubKey, types.Description{}) + validator.Tokens = sdk.NewInt(simState.InitialStake) + validator.DelegatorShares = sdk.NewDec(simState.InitialStake) + delegation := types.NewDelegation(simState.Accounts[i].Address, valAddr, sdk.NewDec(simState.InitialStake)) + validators = append(validators, validator) + delegations = append(delegations, delegation) + } + + stakingGenesis := types.NewGenesisState(params, validators, delegations) + + fmt.Printf("Selected randomly generated staking parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, stakingGenesis.Params)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(stakingGenesis) +} diff --git a/x/staking/simulation/params.go b/x/staking/simulation/params.go new file mode 100644 index 000000000000..f4c292cae674 --- /dev/null +++ b/x/staking/simulation/params.go @@ -0,0 +1,33 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +const ( + keyMaxValidators = "MaxValidators" + keyUnbondingTime = "UnbondingTime" +) + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation +func ParamChanges(r *rand.Rand) []simulation.ParamChange { + return []simulation.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, keyMaxValidators, "", + func(r *rand.Rand) string { + return fmt.Sprintf("%d", GenMaxValidators(r)) + }, + ), + simulation.NewSimParamChange(types.ModuleName, keyUnbondingTime, "", + func(r *rand.Rand) string { + return fmt.Sprintf("\"%d\"", GenUnbondingTime(r)) + }, + ), + } +} diff --git a/x/supply/module.go b/x/supply/module.go index 5607b4d3207d..6c14e8d892cd 100644 --- a/x/supply/module.go +++ b/x/supply/module.go @@ -2,6 +2,7 @@ package supply import ( "encoding/json" + "math/rand" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -12,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + sim "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/cosmos/cosmos-sdk/x/supply/client/cli" "github.com/cosmos/cosmos-sdk/x/supply/client/rest" "github.com/cosmos/cosmos-sdk/x/supply/internal/types" @@ -21,7 +23,7 @@ import ( var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModuleSimulation = AppModule{} ) // AppModuleBasic defines the basic application module used by the supply module. @@ -76,6 +78,16 @@ func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[StoreKey] = simulation.DecodeStore } +// GenerateGenesisState creates a randomized GenState of the supply module. +func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RandomizedParams doesn't create any randomized supply param changes for the simulator. +func (AppModuleSimulation) RandomizedParams(_ *rand.Rand) []sim.ParamChange { + return nil +} + //____________________________________________________________________________ // AppModule implements an application module for the supply module. diff --git a/x/supply/simulation/genesis.go b/x/supply/simulation/genesis.go new file mode 100644 index 000000000000..53775fbc07d6 --- /dev/null +++ b/x/supply/simulation/genesis.go @@ -0,0 +1,23 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/supply/internal/types" +) + +// RandomizedGenState generates a random GenesisState for supply +func RandomizedGenState(simState *module.SimulationState) { + numAccs := int64(len(simState.Accounts)) + totalSupply := sdk.NewInt(simState.InitialStake * (numAccs + simState.NumBonded)) + supplyGenesis := types.NewGenesisState(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, totalSupply))) + + fmt.Printf("Generated supply parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, supplyGenesis)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis) +}