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 ""
+ }
+}