From e7978b4b3c93f807ba63d6c79bed4a010dec63c6 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Thu, 18 Jul 2024 14:51:42 +0200 Subject: [PATCH] Start adding an extension to the simulator --- .github/workflows/simulation.yml | 31 ++++- Makefile | 8 +- app/provider/no_inactive_vals_params.json | 4 + app/provider/sim_test.go | 5 + x/ccv/provider/keeper/invariants.go | 111 ++++++++++++++++++ .../keeper/staking_keeper_interface.go | 2 +- x/ccv/provider/module.go | 1 + x/ccv/provider/simulation/decoder.go | 15 +++ 8 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 app/provider/no_inactive_vals_params.json create mode 100644 x/ccv/provider/simulation/decoder.go diff --git a/.github/workflows/simulation.yml b/.github/workflows/simulation.yml index 72c3a70b2c..9fb0a7ae61 100644 --- a/.github/workflows/simulation.yml +++ b/.github/workflows/simulation.yml @@ -17,7 +17,7 @@ concurrency: cancel-in-progress: true jobs: - simulation: + full: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -38,7 +38,32 @@ jobs: **/go.sum **/Makefile Makefile - - name: simulation test + - name: full simulation test if: env.GIT_DIFF run: | - make sim-full \ No newline at end of file + make sim-full + no-inactive: + 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/get-diff-action@v6.1.2 + id: git_diff + with: + PATTERNS: | + **/*.go + go.mod + go.sum + **/go.mod + **/go.sum + **/Makefile + Makefile + - name: simulation test without inactive validators + if: env.GIT_DIFF + run: | + make sim-full-no-inactive-vals \ No newline at end of file diff --git a/Makefile b/Makefile index c00d2fcb23..1e8eb8e5bc 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,13 @@ verify-models: # 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 + go test -mod=readonly . -run=^TestFullAppSimulation$ -Enabled=true -NumBlocks=500 -BlockSize=200 -Commit=true -timeout 24h -v + +# Run full simulation without any inactive validators +sim-full-no-inactive-vals: + cd app/provider;\ + go test -mod=readonly . -run=^TestFullAppSimulation$ -Enabled=true -NumBlocks=500 -BlockSize=200 -Commit=true -timeout 24h -Params=no_inactive_vals_params.json -v + ############################################################################### ### Linting ### diff --git a/app/provider/no_inactive_vals_params.json b/app/provider/no_inactive_vals_params.json new file mode 100644 index 0000000000..127beaca79 --- /dev/null +++ b/app/provider/no_inactive_vals_params.json @@ -0,0 +1,4 @@ +{ + "max_validators": 100, + "max_provider_consensus_validators": 100 +} \ No newline at end of file diff --git a/app/provider/sim_test.go b/app/provider/sim_test.go index e9d23807c7..a6eea50c6c 100644 --- a/app/provider/sim_test.go +++ b/app/provider/sim_test.go @@ -1,6 +1,7 @@ package app_test import ( + "fmt" "os" "testing" @@ -39,6 +40,10 @@ func TestFullAppSimulation(t *testing.T) { config := simcli.NewConfigFromFlags() config.ChainID = "provi" + fmt.Println("========================================") + fmt.Println(config) + fmt.Println("========================================") + db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "leveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue) if skip { t.Skip("skipping application simulation") diff --git a/x/ccv/provider/keeper/invariants.go b/x/ccv/provider/keeper/invariants.go index ca73ffad25..e48a2822b3 100644 --- a/x/ccv/provider/keeper/invariants.go +++ b/x/ccv/provider/keeper/invariants.go @@ -4,6 +4,7 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" types "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) @@ -11,6 +12,9 @@ import ( func RegisterInvariants(ir sdk.InvariantRegistry, k *Keeper) { ir.RegisterRoute(types.ModuleName, "max-provider-validators", MaxProviderConsensusValidatorsInvariant(k)) + + ir.RegisterRoute(types.ModuleName, "staking-keeper-equivalence", + StakingKeeperEquivalenceInvariant(*k)) } // MaxProviderConsensusValidatorsInvariant checks that the number of provider consensus validators @@ -30,3 +34,110 @@ func MaxProviderConsensusValidatorsInvariant(k *Keeper) sdk.Invariant { return "", false } } + +// StakingKeeperEquivalenceInvariant checks that *if* MaxProviderConsensusValidators == MaxValidators, then +// the staking keeper and the provider keeper +// return the same values for their common interface, +// i.e. the functions from staking_keeper_interface.go +func StakingKeeperEquivalenceInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + stakingKeeper := k.stakingKeeper + + maxValidators, err := stakingKeeper.MaxValidators(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting max validators from staking keeper: %v", err)), true + } + + maxProviderConsensusValidators := k.GetParams(ctx).MaxProviderConsensusValidators + + fmt.Println(k.GetParams(ctx)) + + if maxValidators != uint32(maxProviderConsensusValidators) { + // the invariant should only check something if these two numbers are equal, + // so skip in this case + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("maxValidators: %v, maxProviderVals: %v", maxValidators, maxProviderConsensusValidators)), true + } + + // check that the staking keeper and the provider keeper return the same values + // for the common interface functions + + // Check IterateBondedValidatorsByPower + providerBondedValidators := make([]stakingtypes.Validator, 0) + err = k.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { + providerBondedValidators = append(providerBondedValidators, validator.(stakingtypes.Validator)) + return false + }) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting provider bonded validators: %v", err)), true + } + + stakingBondedValidators := make([]stakingtypes.Validator, 0) + err = stakingKeeper.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { + stakingBondedValidators = append(stakingBondedValidators, validator.(stakingtypes.Validator)) + return false + }) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting staking bonded validators: %v", err)), true + } + + if len(providerBondedValidators) != len(stakingBondedValidators) { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("provider bonded validators: %v, staking bonded validators: %v", + providerBondedValidators, stakingBondedValidators)), true + } + + for i, providerVal := range providerBondedValidators { + stakingVal := stakingBondedValidators[i] + + if providerVal.Equal(&stakingVal) { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("provider validator: %v, staking validator: %v", + providerVal.GetOperator(), stakingVal.GetOperator())), true + } + } + + // Check TotalBondedTokens + providerTotalBondedTokens, err := k.TotalBondedTokens(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting provider total bonded tokens: %v", err)), true + } + + stakingTotalBondedTokens, err := stakingKeeper.TotalBondedTokens(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting staking total bonded tokens: %v", err)), true + } + + if !providerTotalBondedTokens.Equal(stakingTotalBondedTokens) { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("provider total bonded tokens: %v, staking total bonded tokens: %v", + providerTotalBondedTokens, stakingTotalBondedTokens)), true + } + + // Check BondedRatio + providerBondedRatio, err := k.BondedRatio(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting provider bonded ratio: %v", err)), true + } + + stakingBondedRatio, err := stakingKeeper.BondedRatio(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting staking bonded ratio: %v", err)), true + } + + if !providerBondedRatio.Equal(stakingBondedRatio) { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("provider bonded ratio: %v, staking bonded ratio: %v", + providerBondedRatio, stakingBondedRatio)), true + } + + return "", false + } +} diff --git a/x/ccv/provider/keeper/staking_keeper_interface.go b/x/ccv/provider/keeper/staking_keeper_interface.go index 60786bb030..eeb12859a1 100644 --- a/x/ccv/provider/keeper/staking_keeper_interface.go +++ b/x/ccv/provider/keeper/staking_keeper_interface.go @@ -40,7 +40,7 @@ func (k Keeper) TotalBondedTokens(ctx context.Context) (math.Int, error) { return false }) - return totalBondedTokens, nil + return totalBondedTokens.Sub(math.NewInt(100)), nil } // IterateDelegations is the same as IterateDelegations in the StakingKeeper. diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index 66a6ee28f6..5041df099e 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -200,6 +200,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { // RegisterStoreDecoder registers a decoder for provider module's types func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) { + // TODO: implement? } // WeightedOperations returns the all the provider module operations with their respective weights. diff --git a/x/ccv/provider/simulation/decoder.go b/x/ccv/provider/simulation/decoder.go new file mode 100644 index 0000000000..bb70bd949e --- /dev/null +++ b/x/ccv/provider/simulation/decoder.go @@ -0,0 +1,15 @@ +package simulation + +import ( + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/gogo/protobuf/codec" +) + +// NewDecodeStore returns a decoder function closure that umarshals the KVPair's +// Value to the corresponding authz type. +func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + panic("TO BE IMPLEMENTED") + return "" + } +}