Skip to content

Commit

Permalink
Start adding e2e test for governance
Browse files Browse the repository at this point in the history
  • Loading branch information
p-offtermatt committed Jul 19, 2024
1 parent 4caa577 commit 42f86bb
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 6 deletions.
15 changes: 14 additions & 1 deletion docs/docs/adrs/adr-017-allowing-inactive-validators.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,20 @@ The validator should be neither slashed nor jailed for downtime.
This can be tested by having an inactive validator go offline on a consumer chain for long enough to accrue downtime.
The consumer chain should send a SlashPacket to the provider chain, which should jail the validator.

* **Mint**:
### Scenario 6: Mint does not consider inactive validators

To compute the inflation rate, only the active validators should be considered.

We can check this by querying the inflation rate from the mint module twice:
Once with all validators active, and once with a lot of stake inactive.

### Scenarios 7: Inactive validators can validate on consumer chains

An inactive validator can opt in and validate on consumer chains (if min stake and max rank allow it)

### Scenario 8: MinStake and MaxRank parameters are respected

Validators that don't meet the criteria for a consumer chain cannot validate on it.

## Consequences

Expand Down
38 changes: 38 additions & 0 deletions tests/e2e/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ const (
CompatibilityTestCfg TestConfigType = "compatibility"
SmallMaxValidatorsTestCfg TestConfigType = "small-max-validators"
InactiveProviderValsTestCfg TestConfigType = "inactive-provider-vals"
GovTestCfg TestConfigType = "gov"
InactiveValsGovTestCfg TestConfigType = "inactive-vals-gov"
)

type TestConfig struct {
Expand Down Expand Up @@ -183,6 +185,10 @@ func GetTestConfig(cfgType TestConfigType, providerVersion, consumerVersion stri
testCfg = SmallMaxValidatorsTestConfig()
case InactiveProviderValsTestCfg:
testCfg = InactiveProviderValsTestConfig()
case GovTestCfg:
testCfg = GovTestConfig()
case InactiveValsGovTestCfg:
testCfg = InactiveValsGovTestConfig()
default:
panic(fmt.Sprintf("Invalid test config: %s", cfgType))
}
Expand Down Expand Up @@ -597,6 +603,38 @@ func SmallMaxValidatorsTestConfig() TestConfig {
return cfg
}

func GovTestConfig() TestConfig {
cfg := DefaultTestConfig()

// set the MaxValidators to 2
proviConfig := cfg.chainConfigs[ChainID("provi")]
proviConfig.GenesisChanges += "| .app_state.gov.params.quorum = \"0.5\""
cfg.chainConfigs[ChainID("provi")] = proviConfig

carolConfig := cfg.validatorConfigs["carol"]
// make carol use her own key
carolConfig.UseConsumerKey = false
cfg.validatorConfigs["carol"] = carolConfig

return cfg
}

func InactiveValsGovTestConfig() TestConfig {
cfg := InactiveProviderValsTestConfig()

// set the MaxValidators to 2
proviConfig := cfg.chainConfigs[ChainID("provi")]
proviConfig.GenesisChanges += "| .app_state.gov.params.quorum = \"0.5\""
cfg.chainConfigs[ChainID("provi")] = proviConfig

carolConfig := cfg.validatorConfigs["carol"]
// make carol use her own key
carolConfig.UseConsumerKey = false
cfg.validatorConfigs["carol"] = carolConfig

return cfg
}

func MultiConsumerTestConfig() TestConfig {
tr := TestConfig{
name: string(MulticonsumerTestCfg),
Expand Down
12 changes: 12 additions & 0 deletions tests/e2e/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,18 @@ var stepChoices = map[string]StepChoice{
description: "test inactive validators on consumer",
testConfig: InactiveProviderValsTestCfg,
},
"inactive-provider-validators-governance": {
name: "inactive-provider-validators-governance",
steps: stepsInactiveProviderValidatorsGovernance(),
description: "test governance with inactive validators",
testConfig: InactiveValsGovTestCfg,
},
"inactive-provider-validators-governance-basecase": {
name: "inactive-provider-validators-governance-basecase",
steps: stepsInactiveProviderValidatorsGovernanceComparison(),
description: "comparison for governance when there are *no* inactive validators, to verify the difference to the governance test *with* inactive validators",
testConfig: GovTestCfg,
},
}

func getTestCaseUsageString() string {
Expand Down
168 changes: 168 additions & 0 deletions tests/e2e/steps_inactive_vals.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,3 +499,171 @@ func setupOptInChain() []Step {
},
}
}

func stepsInactiveProviderValidatorsGovernance() []Step {
s := concatSteps(
[]Step{
{
Action: StartChainAction{
Chain: ChainID("provi"),
Validators: []StartChainValidator{
{Id: ValidatorID("alice"), Stake: 290000000, Allocation: 10000000000},
{Id: ValidatorID("bob"), Stake: 290000000, Allocation: 10000000000},
{Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000},
},
},
State: State{
ChainID("provi"): ChainState{
ValPowers: &map[ValidatorID]uint{
ValidatorID("alice"): 0, // max consensus validators is 1, so alice and bob should not be in power
ValidatorID("bob"): 0,
ValidatorID("carol"): 300,
},
StakedTokens: &map[ValidatorID]uint{
ValidatorID("alice"): 290000000,
ValidatorID("bob"): 290000000,
ValidatorID("carol"): 300000000,
},
},
},
},
},
[]Step{
// create a governance proposal
{
Action: SubmitConsumerAdditionProposalAction{
Chain: ChainID("provi"),
From: ValidatorID("alice"),
Deposit: 10000001,
ConsumerChain: ChainID("consu"),
SpawnTime: 0,
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
TopN: 51,
},
State: State{
ChainID("provi"): ChainState{
Proposals: &map[uint]Proposal{
1: ConsumerAdditionProposal{
Deposit: 10000001,
Chain: ChainID("consu"),
SpawnTime: 0,
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)),
},
},
},
},
},
// vote for it with carol
{
Action: VoteGovProposalAction{
Chain: ChainID("provi"),
From: []ValidatorID{ValidatorID("carol")},
Vote: []string{"yes"},
PropNumber: 1,
},
State: State{
ChainID("provi"): ChainState{
Proposals: &map[uint]Proposal{
1: ConsumerAdditionProposal{
Deposit: 10000001,
Chain: ChainID("consu"),
SpawnTime: 0,
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
// the proposal should have passed because carol voted for it.
// carol alone is enough to pass the quorum, because stake of the other validators is not counted
Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_PASSED)),
},
},
},
},
},
},
)

return s
}

func stepsInactiveProviderValidatorsGovernanceComparison() []Step {
s := concatSteps(
[]Step{
{
Action: StartChainAction{
Chain: ChainID("provi"),
Validators: []StartChainValidator{
{Id: ValidatorID("alice"), Stake: 290000000, Allocation: 10000000000},
{Id: ValidatorID("bob"), Stake: 290000000, Allocation: 10000000000},
{Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000},
},
},
State: State{
ChainID("provi"): ChainState{
ValPowers: &map[ValidatorID]uint{
ValidatorID("alice"): 290,
ValidatorID("bob"): 290,
ValidatorID("carol"): 300,
},
StakedTokens: &map[ValidatorID]uint{
ValidatorID("alice"): 290000000,
ValidatorID("bob"): 290000000,
ValidatorID("carol"): 300000000,
},
},
},
},
},
[]Step{
// create a governance proposal
{
Action: SubmitConsumerAdditionProposalAction{
Chain: ChainID("provi"),
From: ValidatorID("alice"),
Deposit: 10000001,
ConsumerChain: ChainID("consu"),
SpawnTime: 0,
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
TopN: 51,
},
State: State{
ChainID("provi"): ChainState{
Proposals: &map[uint]Proposal{
1: ConsumerAdditionProposal{
Deposit: 10000001,
Chain: ChainID("consu"),
SpawnTime: 0,
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)),
},
},
},
},
},
// vote for it with carol
{
Action: VoteGovProposalAction{
Chain: ChainID("provi"),
From: []ValidatorID{ValidatorID("carol")},
Vote: []string{"yes"},
PropNumber: 1,
},
State: State{
ChainID("provi"): ChainState{
Proposals: &map[uint]Proposal{
1: ConsumerAdditionProposal{
Deposit: 10000001,
Chain: ChainID("consu"),
SpawnTime: 0,
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
// the proposal should *not* have passed because only carol voted for it,
// and carol is not enough to pass the quorum
Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_FAILED)),
},
},
},
},
},
},
)

return s
}
15 changes: 10 additions & 5 deletions tests/e2e/steps_partial_set_security.go
Original file line number Diff line number Diff line change
Expand Up @@ -1511,8 +1511,10 @@ func stepsValidatorsAllowlistedChain() []Step {
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
TopN: 0,
// only "alice" and "bob" are allowlisted (see `getDefaultValidators` in `tests/e2e/config.go`)
Allowlist: []string{"cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq",
"cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39"},
Allowlist: []string{
"cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq",
"cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39",
},
},
State: State{
ChainID("provi"): ChainState{
Expand Down Expand Up @@ -2249,8 +2251,10 @@ func stepsModifyChain() []Step {
Deposit: 10000001,
ConsumerChain: ChainID("consu"),
// only "alice" and "carol" are allowlisted (see `getDefaultValidators` in `tests/e2e/config.go`)
Allowlist: []string{"cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq",
"cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6"},
Allowlist: []string{
"cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq",
"cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6",
},
},
State: State{
ChainID("provi"): ChainState{
Expand Down Expand Up @@ -2437,7 +2441,8 @@ func stepsModifyChain() []Step {
ExpectError: true, // because this chain is now Top 100%, no validator can opt out
},
State: State{},
}}
},
}

return s
}

0 comments on commit 42f86bb

Please sign in to comment.