Skip to content

Commit

Permalink
test: Add an e2e test that reproduces the chain halt (#1942)
Browse files Browse the repository at this point in the history
* Start writing e2e test with unjailing

* Add e2e steps for too many validators bug

* Fix test config and setup

* Change test to use top N chain

* Add comment for panic

* Start cleaning up active/inactive vals e2e test

* Revert change to StartChains

* Revert changes to partial-set-security tests

* Rename test case

* Rename CLI flag for test case

* Address comments

* Add active set changes test to nightly runs

* Fix merge in main.go
  • Loading branch information
p-offtermatt authored Jun 11, 2024
1 parent 8a8e7a0 commit 198a26f
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/nightly-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,22 @@ jobs:
go-version: "1.21" # The Go version to download (if necessary) and use.
- name: E2E partial set security modification proposal
run: go run ./tests/e2e/... --tc partial-set-security-modification-proposal
active-set-changes-test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
- uses: actions/checkout@v4
- name: Checkout LFS objects
run: git lfs checkout
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.21" # The Go version to download (if necessary) and use.
- name: E2E active set changes
run: go run ./tests/e2e/... --tc active-set-changes

nightly-test-fail:
needs:
Expand All @@ -311,7 +327,11 @@ jobs:
- partial-set-security-validators-power-cap-test
- partial-set-security-validators-allowlisted-test
- partial-set-security-validators-denylisted-test
<<<<<<< HEAD
- active-set-changes-test
=======
- partial-set-security-modification-proposal
>>>>>>> main
if: ${{ failure() }}
runs-on: ubuntu-latest
steps:
Expand Down
19 changes: 19 additions & 0 deletions tests/e2e/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const (
MulticonsumerTestCfg TestConfigType = "multi-consumer"
ConsumerMisbehaviourTestCfg TestConfigType = "consumer-misbehaviour"
CompatibilityTestCfg TestConfigType = "compatibility"
SmallMaxValidatorsTestCfg TestConfigType = "small-max-validators"
)

// Attributes that are unique to a validator. Allows us to map (part of)
Expand Down Expand Up @@ -264,6 +265,8 @@ func GetTestConfig(cfgType TestConfigType, providerVersion, consumerVersion stri
testCfg = ConsumerMisbehaviourTestConfig()
case CompatibilityTestCfg:
testCfg = CompatibilityTestConfig(pv, cv)
case SmallMaxValidatorsTestCfg:
testCfg = SmallMaxValidatorsTestConfig()
default:
panic(fmt.Sprintf("Invalid test config: %s", cfgType))
}
Expand Down Expand Up @@ -605,6 +608,22 @@ func DemocracyTestConfig(allowReward bool) TestConfig {
return tr
}

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

// set the MaxValidators to 2
proviConfig := cfg.chainConfigs[ChainID("provi")]
proviConfig.GenesisChanges += "| .app_state.staking.params.max_validators = 2"
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
7 changes: 7 additions & 0 deletions tests/e2e/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ var stepChoices = map[string]StepChoice{
description: "test partial set security for an Opt-In chain that has a validator denylisted",
testConfig: DefaultTestCfg,
},
"active-set-changes": {
name: "active-set-changes",
steps: stepsActiveSetChanges(),
description: "This is a regression test related to the issue discussed here: https://forum.cosmos.network/t/cosmos-hub-v17-1-chain-halt-post-mortem/13899. The test ensures that the protocol works as expected when MaxValidators is smaller than the number of potential validators.",
testConfig: SmallMaxValidatorsTestCfg,
},
"partial-set-security-modification-proposal": {
name: "partial-set-security-modification-proposal",
steps: stepsModifyChain(),
Expand Down Expand Up @@ -286,6 +292,7 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe
"consumer-double-downtime", "partial-set-security-opt-in", "partial-set-security-top-n",
"partial-set-security-validator-set-cap", "partial-set-security-validators-power-cap",
"partial-set-security-validators-allowlisted", "partial-set-security-validators-denylisted",
"active-set-changes",
"partial-set-security-modification-proposal",
}
if includeMultiConsumer != nil && *includeMultiConsumer {
Expand Down
138 changes: 138 additions & 0 deletions tests/e2e/steps_active_set_changes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package main

import clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"

// stepsActiveSetChanges starts a top N provider chain and causes a change in the active set
func stepsActiveSetChanges() []Step {
s := []Step{
// === setup provider chain, consumer chain with top N = 100, and start IBC connections ===
{
Action: StartChainAction{
Chain: ChainID("provi"),
Validators: []StartChainValidator{
{Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000},
{Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000},
{Id: ValidatorID("carol"), Stake: 700000000, Allocation: 10000000000},
},
},
State: State{
ChainID("provi"): ChainState{
ValPowers: &map[ValidatorID]uint{
ValidatorID("alice"): 0, // MaxValidators is set to 2, so alice is not part of the validator set
ValidatorID("bob"): 200,
ValidatorID("carol"): 700,
},
},
},
},
{
Action: SubmitConsumerAdditionProposalAction{
Chain: ChainID("provi"),
From: ValidatorID("alice"),
Deposit: 10000001,
ConsumerChain: ChainID("consu"),
SpawnTime: 0,
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
TopN: 100,
},
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: "PROPOSAL_STATUS_VOTING_PERIOD",
},
},
HasToValidate: &map[ValidatorID][]ChainID{
ValidatorID("alice"): {},
ValidatorID("bob"): {},
ValidatorID("carol"): {},
},
},
},
},
{
Action: VoteGovProposalAction{
Chain: ChainID("provi"),
From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")},
Vote: []string{"yes", "yes", "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},
Status: "PROPOSAL_STATUS_PASSED",
},
},
},
},
},
{
// we start all the validators but only "alice" and "bob" have opted in and hence
// only "alice" and "bob" are validating blocks
Action: StartConsumerChainAction{
ConsumerChain: ChainID("consu"),
ProviderChain: ChainID("provi"),
Validators: []StartChainValidator{
{Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000},
{Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000},
{Id: ValidatorID("carol"), Stake: 700000000, Allocation: 10000000000},
},
// For consumers that're launching with the provider being on an earlier version
// of ICS before the soft opt-out threshold was introduced, we need to set the
// soft opt-out threshold to 0.05 in the consumer genesis to ensure that the
// consumer binary doesn't panic. Sdk requires that all params are set to valid
// values from the genesis file.
GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"",
},
State: State{},
},
{
Action: AddIbcConnectionAction{
ChainA: ChainID("consu"),
ChainB: ChainID("provi"),
ClientA: 0,
ClientB: 0,
},
State: State{},
},
{
Action: AddIbcChannelAction{
ChainA: ChainID("consu"),
ChainB: ChainID("provi"),
ConnectionA: 0,
PortA: "consumer",
PortB: "provider",
Order: "ordered",
},
State: State{},
},
// === setup ends, ready for the actual test ===
// bob incurs downtime on the provider and gets jailed
{
Action: DowntimeSlashAction{
Chain: ChainID("provi"),
Validator: ValidatorID("bob"),
},
State: State{
ChainID("provi"): ChainState{
ValPowers: &map[ValidatorID]uint{
ValidatorID("alice"): 100, // alice goes into the active set
ValidatorID("bob"): 0,
ValidatorID("carol"): 700,
},
},
},
},
}

return s
}

0 comments on commit 198a26f

Please sign in to comment.