Skip to main content

Module Simulation

Pre-requisite Readings

Synopsis​

This document details how to define each module simulation functions to be integrated with the application SimulationManager.

Simulation package​

Every module that implements the Cosmos SDK simulator needs to have a x/<module>/simulation package which contains the primary functions required by the fuzz tests: store decoders, randomized genesis state and parameters, weighted operations and proposal contents.

Store decoders​

Registering the store decoders is required for the AppImportExport. This allows for the key-value pairs from the stores to be decoded (i.e unmarshalled) to their corresponding types. In particular, it matches the key to a concrete type and then unmarshals the value from the KVPair to the type provided.

You can use the example here from the distribution module to implement your store decoders.

Randomized genesis​

The simulator tests different scenarios and values for genesis parameters in order to fully test the edge cases of specific modules. The simulator package from each module must expose a RandomizedGenState function to generate the initial random GenesisState from a given seed.

Once the module genesis parameter are generated randomly (or with the key and values defined in a params file), they are marshaled to JSON format and added to the app genesis JSON to use it on the simulations.

You can check an example on how to create the randomized genesis here.

Random weighted operations​

Operations are one of the crucial parts of the Cosmos SDK simulation. They are the transactions (Msg) that are simulated with random field values. The sender of the operation is also assigned randomly.

Operations on the simulation are simulated using the full transaction cycle of a ABCI application that exposes the BaseApp.

Shown below is how weights are set:

https://github.com/cosmos/cosmos-sdk/blob/release/v0.50.x/x/staking/simulation/operations.go#L19-L86

As you can see, the weights are predefined in this case. Options exist to override this behavior with different weights. One option is to use *rand.Rand to define a random weight for the operation, or you can inject your own predefined weights.

Here is how one can override the above package simappparams.

https://github.com/cosmos/cosmos-sdk/blob/release/v0.51.x/Makefile#L292-L334

The SDK simulations can be executed like normal tests in Go from the shell or within an IDE. Make sure that you pass the -tags='sims parameter to enable them and other params that make sense for your scenario.

Random proposal contents​

Randomized governance proposals are also supported on the Cosmos SDK simulator. Each module must define the governance proposal Contents that they expose and register them to be used on the parameters.

Registering simulation functions​

Now that all the required functions are defined, we need to integrate them into the module pattern within the module.go:

https://github.com/cosmos/cosmos-sdk/blob/release/v0.50.x/x/distribution/module.go#L180-L203

App Simulator manager​

The following step is setting up the SimulatorManager at the app level. This is required for the simulation test files on the next step.

type CustomApp struct {
...
sm *module.SimulationManager
}

Then at the instantiation of the application, we create the SimulationManager instance in the same way we create the ModuleManager but this time we only pass the modules that implement the simulation functions from the AppModuleSimulation interface described above.

func NewCustomApp(...) {
// create the simulation manager and define the order of the modules for deterministic simulations
app.sm = module.NewSimulationManager(
auth.NewAppModule(app.accountKeeper),
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
supply.NewAppModule(app.supplyKeeper, app.accountKeeper),
gov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper),
mint.NewAppModule(app.mintKeeper),
distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper),
staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper),
slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper),
)

// register the store decoders for simulation tests
app.sm.RegisterStoreDecoders()
...
}

Integration with the Go fuzzer framework​

The simulations provide deterministic behaviour already. The integration with the Go fuzzer can be done at a high level with the deterministic pseudo random number generator where the fuzzer provides varying numbers.

https://github.com/cosmos/cosmos-sdk/blob/release/v0.51.x/Makefile#L352-L355