Skip to content

Commit

Permalink
test: enable the simulator for the provider module (#2005)
Browse files Browse the repository at this point in the history
* Refactor validator set storage

* Add comment for getTotalPower

* Add provider consensus validator set storage

* Add new MaxProviderConsensusValidators param

* Add validation for MaxProviderConsensusValidators

* Add no_valupdates_staking module

* Add function to get MaxProviderConsensusValidators param

* Start returning validators in EndBlock

* Fix tests

* Revert cosmetic change

* Revert cosmetic changes

* Revert formatting

* Add genutil replacer module

* Revert formatting

* Revert formatting in tests/integration

* Revert minor formatting

* Fix type

* Change wrapped staking to conform to EndBlocker interface

* Fix typo

* Revert "Fix typo"

This reverts commit 62dfd1e.

* Add e2e test for inactive vals

* Start fixing e2e test

* Revert formatting changes

* Remove more formatting

* Revert extra formatting

* Re-wire provider/app.go to use wrapped modules

* Remove consumer rewards check

* Add simulator test

* Add randomly generated parameters for provider in sim

* Add invariant

* Add simulation to Makefile and github workflow

* Use simcli instead of just passing true
  • Loading branch information
p-offtermatt committed Jul 22, 2024
1 parent e71129e commit 9788053
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 5 deletions.
44 changes: 44 additions & 0 deletions .github/workflows/simulation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Simulation
on:
workflow_call:
pull_request:
merge_group:
push:
branches:
- main
- release/v*
- feat/*

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}-tests
cancel-in-progress: true

jobs:
simulation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22"
check-latest: true
cache: true
cache-dependency-path: go.sum
- uses: technote-space/[email protected]
id: git_diff
with:
PATTERNS: |
**/*.go
go.mod
go.sum
**/go.mod
**/go.sum
**/Makefile
Makefile
- name: simulation test
if: env.GIT_DIFF
run: |
make sim-full
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ verify-models:
../run_invariants.sh


###############################################################################
### Simulation tests ###

# Run a full simulation test
sim-full:
cd app/provider;\
go test -mod=readonly -run=^TestFullAppSimulation$ -Enabled=true -NumBlocks=500 -BlockSize=200 -Commit=true -timeout 24h github.com/cosmos/interchain-security/v5/app/provider -v

###############################################################################
### Linting ###
Expand Down
21 changes: 18 additions & 3 deletions app/provider/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import (
authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
"github.com/cosmos/cosmos-sdk/x/auth/posthandler"
authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
Expand Down Expand Up @@ -352,9 +353,7 @@ func New(
// Remove the ConsumerRewardsPool from the group of blocked recipient addresses in bank
// this is required for the provider chain to be able to receive tokens from
// the consumer chain
bankBlockedAddrs := app.ModuleAccountAddrs()
delete(bankBlockedAddrs, authtypes.NewModuleAddress(
providertypes.ConsumerRewardsPool).String())
bankBlockedAddrs := BankBlockedAddrs(app)

app.BankKeeper = bankkeeper.NewBaseKeeper(
appCodec,
Expand Down Expand Up @@ -678,6 +677,15 @@ func New(
panic(err)
}

// create the simulation manager and define the order of the modules for deterministic simulations
overrideModules := map[string]module.AppModuleSimulation{
authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)),
}
app.sm = module.NewSimulationManagerFromAppModules(app.MM.Modules, overrideModules)

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

// Note this upgrade handler is just an example and may not be exactly what you need to implement.
// See https://docs.cosmos.network/v0.45/building-modules/upgrade.html
app.UpgradeKeeper.SetUpgradeHandler(
Expand Down Expand Up @@ -778,6 +786,13 @@ func New(
return app
}

func BankBlockedAddrs(app *App) map[string]bool {
bankBlockedAddrs := app.ModuleAccountAddrs()
delete(bankBlockedAddrs, authtypes.NewModuleAddress(
providertypes.ConsumerRewardsPool).String())
return bankBlockedAddrs
}

// Name returns the name of the App
func (app *App) Name() string { return app.BaseApp.Name() }

Expand Down
85 changes: 85 additions & 0 deletions app/provider/sim_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package app_test

import (
"os"
"testing"

"cosmossdk.io/store"
"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/server"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
"github.com/cosmos/cosmos-sdk/x/simulation"
simcli "github.com/cosmos/cosmos-sdk/x/simulation/client/cli"

simtypes "github.com/cosmos/cosmos-sdk/types/simulation"

providerapp "github.com/cosmos/interchain-security/v5/app/provider"
)

func init() {
simcli.GetSimulatorFlags()
}

// interBlockCacheOpt returns a BaseApp option function that sets the persistent
// inter-block write-through cache.
func interBlockCacheOpt() func(*baseapp.BaseApp) {
return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager())
}

// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of
// an IAVLStore for faster simulation speed.
func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
bapp.SetFauxMerkleMode()
}

func TestFullAppSimulation(t *testing.T) {
config := simcli.NewConfigFromFlags()
config.ChainID = "provi"

db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "leveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue)
if skip {
t.Skip("skipping application simulation")
}
require.NoError(t, err, "simulation setup failed")

defer func() {
require.NoError(t, db.Close())
require.NoError(t, os.RemoveAll(dir))
}()

appOptions := make(simtestutil.AppOptionsMap, 0)
appOptions[flags.FlagHome] = providerapp.DefaultNodeHome
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue

app := providerapp.New(logger, db, nil, true, appOptions, fauxMerkleModeOpt, interBlockCacheOpt(), baseapp.SetChainID("provi"))
require.Equal(t, "interchain-security-p", app.Name())

encoding := providerapp.MakeTestEncodingConfig()

genesisState := providerapp.NewDefaultGenesisState(encoding.Codec)

// run randomized simulation
_, simParams, simErr := simulation.SimulateFromSeed(
t,
os.Stdout,
app.BaseApp,
simtestutil.AppStateFn(encoding.Codec, app.SimulationManager(), genesisState),
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
simtestutil.SimulationOperations(app, app.AppCodec(), config),
providerapp.BankBlockedAddrs(app),
config,
app.AppCodec(),
)

// export state and simParams before the simulation error is checked
err = simtestutil.CheckExportSimulation(app, config, simParams)
require.NoError(t, err)
require.NoError(t, simErr)

if config.Commit {
simtestutil.PrintStats(db)
}
}
32 changes: 32 additions & 0 deletions x/ccv/provider/keeper/invariants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package keeper

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
types "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"
)

// RegisterInvariants registers all staking invariants
func RegisterInvariants(ir sdk.InvariantRegistry, k *Keeper) {
ir.RegisterRoute(types.ModuleName, "max-provider-validators",
MaxProviderConsensusValidatorsInvariant(k))
}

// MaxProviderConsensusValidatorsInvariant checks that the number of provider consensus validators
// is less than or equal to the maximum number of provider consensus validators
func MaxProviderConsensusValidatorsInvariant(k *Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
params := k.GetParams(ctx)
maxProviderConsensusValidators := params.MaxProviderConsensusValidators

consensusValidators := k.GetLastProviderConsensusValSet(ctx)
if int64(len(consensusValidators)) > maxProviderConsensusValidators {
return sdk.FormatInvariant(types.ModuleName, "max-provider-validators",
fmt.Sprintf("number of provider consensus validators: %d, exceeds max: %d",
len(consensusValidators), maxProviderConsensusValidators)), true
}

return "", false
}
}
6 changes: 4 additions & 2 deletions x/ccv/provider/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/cosmos/interchain-security/v5/x/ccv/provider/client/cli"
"github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper"
"github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations"
"github.com/cosmos/interchain-security/v5/x/ccv/provider/simulation"
providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"
)

Expand Down Expand Up @@ -115,8 +116,8 @@ func NewAppModule(k *keeper.Keeper, paramSpace paramtypes.Subspace) AppModule {
}

// RegisterInvariants implements the AppModule interface
func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
// TODO
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
keeper.RegisterInvariants(ir, am.keeper)
}

// RegisterServices registers module services.
Expand Down Expand Up @@ -194,6 +195,7 @@ func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error

// GenerateGenesisState creates a randomized GenState of the transfer module.
func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
simulation.RandomizedGenState(simState)
}

// RegisterStoreDecoder registers a decoder for provider module's types
Expand Down
44 changes: 44 additions & 0 deletions x/ccv/provider/simulation/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package simulation

import (
"encoding/json"
"fmt"
"math/rand"

"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/interchain-security/v5/x/ccv/provider/types"
)

// Simulation parameter constants
const (
// only includes params that make sense even with a single
maxProviderConsensusValidators = "max_provider_consensus_validators"
)

// genMaxProviderConsensusValidators returns randomized maxProviderConsensusValidators
func genMaxProviderConsensusValidators(r *rand.Rand) int64 {
return int64(r.Intn(250) + 1)
}

// RandomizedGenState generates a random GenesisState for staking
func RandomizedGenState(simState *module.SimulationState) {
// params
var (
maxProviderConsensusVals int64
)

simState.AppParams.GetOrGenerate(maxProviderConsensusValidators, &maxProviderConsensusVals, simState.Rand, func(r *rand.Rand) { maxProviderConsensusVals = genMaxProviderConsensusValidators(r) })

providerParams := types.DefaultParams()
providerParams.MaxProviderConsensusValidators = maxProviderConsensusVals

providerGenesis := types.DefaultGenesisState()
providerGenesis.Params = providerParams

bz, err := json.MarshalIndent(&providerGenesis.Params, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated provider parameters:\n%s\n", bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(providerGenesis)
}

0 comments on commit 9788053

Please sign in to comment.