diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index fb5f184081..f5b8946188 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -514,9 +514,10 @@ func (tr TestRun) voteGovProposal( } type startConsumerChainAction struct { - consumerChain chainID - providerChain chainID - validators []StartChainValidator + consumerChain chainID + providerChain chainID + validators []StartChainValidator + genesisChanges string } func (tr TestRun) startConsumerChain( @@ -545,7 +546,7 @@ func (tr TestRun) startConsumerChain( consumerGenesis := ".app_state.ccvconsumer = " + string(bz) consumerGenesisChanges := tr.chainConfigs[action.consumerChain].genesisChanges if consumerGenesisChanges != "" { - consumerGenesis = consumerGenesis + " | " + consumerGenesisChanges + consumerGenesis = consumerGenesis + " | " + consumerGenesisChanges + " | " + action.genesisChanges } tr.startChain(StartChainAction{ diff --git a/tests/e2e/step_delegation.go b/tests/e2e/step_delegation.go index d1fb2e9de6..cca10806e7 100644 --- a/tests/e2e/step_delegation.go +++ b/tests/e2e/step_delegation.go @@ -127,8 +127,10 @@ func stepsUnbond(consumerName string) []Step { } } -// stepsRedelegate tests redelegation and resulting validator power changes. -func stepsRedelegate(consumerName string) []Step { +// stepsRedelegateForOptOut tests redelegation, and sets up voting powers s.t +// alice will have less than 5% of the total voting power. This is needed to +// test opt-out functionality. +func stepsRedelegateForOptOut(consumerName string) []Step { return []Step{ { action: redelegateTokensAction{ @@ -136,9 +138,58 @@ func stepsRedelegate(consumerName string) []Step { src: validatorID("alice"), dst: validatorID("carol"), txSender: validatorID("alice"), - // Leave alice with majority stake so non-faulty validators maintain more than + amount: 450000000, + }, + state: State{ + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 60, + validatorID("bob"): 500, + validatorID("carol"): 950, + }, + }, + chainID(consumerName): ChainState{ + ValPowers: &map[validatorID]uint{ + // Voting power changes not seen by consumer yet + validatorID("alice"): 510, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + }, + }, + { + action: relayPacketsAction{ + chain: chainID("provi"), + port: "provider", + channel: 0, + }, + state: State{ + chainID(consumerName): ChainState{ + ValPowers: &map[validatorID]uint{ + // Now power changes are seen by consumer + validatorID("alice"): 60, + validatorID("bob"): 500, + validatorID("carol"): 950, + }, + }, + }, + }, + } +} + +// stepsRedelegate tests redelegation and resulting validator power changes. +func stepsRedelegate(consumerName string) []Step { + return []Step{ + { + action: redelegateTokensAction{ + chain: chainID("provi"), + src: validatorID("carol"), + dst: validatorID("alice"), + txSender: validatorID("carol"), + // redelegate s.t. alice has majority stake so non-faulty validators maintain more than // 2/3 voting power during downtime tests below, avoiding chain halt - amount: 1000000, + amount: 449000000, }, state: State{ chainID("provi"): ChainState{ @@ -152,9 +203,9 @@ func stepsRedelegate(consumerName string) []Step { chainID(consumerName): ChainState{ ValPowers: &map[validatorID]uint{ // Voting power changes not seen by consumer yet - validatorID("alice"): 510, + validatorID("alice"): 60, validatorID("bob"): 500, - validatorID("carol"): 500, + validatorID("carol"): 950, }, }, }, diff --git a/tests/e2e/steps.go b/tests/e2e/steps.go index 448b6d3bce..286115da84 100644 --- a/tests/e2e/steps.go +++ b/tests/e2e/steps.go @@ -18,6 +18,8 @@ var happyPathSteps = concatSteps( stepsDelegate("consu"), stepsAssignConsumerKeyOnStartedChain("consu", "bob"), stepsUnbond("consu"), + stepsRedelegateForOptOut("consu"), + stepsDowntimeWithOptOut("consu"), stepsRedelegate("consu"), stepsDowntime("consu"), stepsRejectEquivocationProposal("consu", 2), // prop to tombstone bob is rejected diff --git a/tests/e2e/steps_downtime.go b/tests/e2e/steps_downtime.go index d478b05052..af2d2f237e 100644 --- a/tests/e2e/steps_downtime.go +++ b/tests/e2e/steps_downtime.go @@ -202,9 +202,62 @@ func stepsDowntime(consumerName string) []Step { }, }, }, + } +} - // TODO: Test full unbonding functionality, tracked as: https://github.com/cosmos/interchain-security/issues/311 - +// stepsDowntimeWithOptOut returns steps validating that alice can incur downtime +// and not be slashed/jailed, since her voting power is less than 5% of the total. +// +// Note: 60 / (60 + 500 + 950) ~= 0.04 +func stepsDowntimeWithOptOut(consumerName string) []Step { + return []Step{ + { + action: downtimeSlashAction{ + chain: chainID(consumerName), + validator: validatorID("alice"), + }, + state: State{ + // powers not affected on either chain + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 60, + validatorID("bob"): 500, + validatorID("carol"): 950, + }, + }, + chainID(consumerName): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 60, + validatorID("bob"): 500, + validatorID("carol"): 950, + }, + }, + }, + }, + { + action: relayPacketsAction{ + chain: chainID("provi"), + port: "provider", + channel: 0, + }, + state: State{ + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + // alice is not slashed or jailed due to soft opt out + validatorID("alice"): 60, + validatorID("bob"): 500, + validatorID("carol"): 950, + }, + }, + chainID(consumerName): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 60, + validatorID("bob"): 500, + validatorID("carol"): 950, + }, + }, + }, + }, } } diff --git a/tests/e2e/steps_start_chains.go b/tests/e2e/steps_start_chains.go index a53e7b4893..e3ba9d9ad8 100644 --- a/tests/e2e/steps_start_chains.go +++ b/tests/e2e/steps_start_chains.go @@ -147,6 +147,12 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint {id: validatorID("alice"), stake: 500000000, allocation: 10000000000}, {id: validatorID("carol"), stake: 500000000, 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{ chainID("provi"): ChainState{