Skip to content

Commit

Permalink
Tests for inactive validators
Browse files Browse the repository at this point in the history
  • Loading branch information
fastfadingviolets committed Aug 27, 2024
1 parent 644549a commit bb96cde
Show file tree
Hide file tree
Showing 6 changed files with 493 additions and 192 deletions.
97 changes: 69 additions & 28 deletions tests/interchain/chainsuite/chain_ics.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/strangelove-ventures/interchaintest/v8/testutil"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
"go.uber.org/multierr"
"golang.org/x/mod/semver"

clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
Expand All @@ -32,6 +33,8 @@ type ConsumerConfig struct {
TopN int
ValidatorSetCap int
ValidatorPowerCap int
AllowInactiveVals bool
MinStake uint64
spec *interchaintest.ChainSpec

DuringDepositPeriod ConsumerBootstrapCb
Expand Down Expand Up @@ -219,7 +222,6 @@ func (p *Chain) DefaultConsumerChainSpec(ctx context.Context, chainID string, co
}
genesisOverrides := []cosmos.GenesisKV{
cosmos.NewGenesisKV("app_state.slashing.params.signed_blocks_window", strconv.Itoa(SlashingWindowConsumer)),
cosmos.NewGenesisKV("consensus_params.block.max_gas", "50000000"),
}
if config.TopN >= 0 {
genesisOverrides = append(genesisOverrides, cosmos.NewGenesisKV("app_state.ccvconsumer.params.soft_opt_out_threshold", "0.0"))
Expand All @@ -235,22 +237,30 @@ func (p *Chain) DefaultConsumerChainSpec(ctx context.Context, chainID string, co
)
}

modifyGenesis := cosmos.ModifyGenesis(genesisOverrides)
if chainType == strideChain {
genesisOverrides = append(genesisOverrides,
cosmos.NewGenesisKV("app_state.gov.params.voting_period", GovVotingPeriod.String()),
)
modifyGenesis = func(cc ibc.ChainConfig, b []byte) ([]byte, error) {
b, err := cosmos.ModifyGenesis(genesisOverrides)(cc, b)
}
modifyGenesis := func(cc ibc.ChainConfig, b []byte) ([]byte, error) {
b, err := cosmos.ModifyGenesis(genesisOverrides)(cc, b)
if err != nil {
return nil, err
}
if chainType == strideChain {
b, err = sjson.SetBytes(b, "app_state.epochs.epochs.#(identifier==\"day\").duration", "120s")
if err != nil {
return nil, err
}
b, err = sjson.SetBytes(b, "app_state.epochs.epochs.#(identifier==\"day\").duration", "120s")
b, err = sjson.SetBytes(b, "app_state.epochs.epochs.#(identifier==\"stride_epoch\").duration", "30s")
if err != nil {
return nil, err
}
return sjson.SetBytes(b, "app_state.epochs.epochs.#(identifier==\"stride_epoch\").duration", "30s")
}
if gjson.GetBytes(b, "consensus").Exists() {
return sjson.SetBytes(b, "consensus.block.max_gas", "50000000")
}
return sjson.SetBytes(b, "consensus_params.block.max_gas", "50000000")
}

return &interchaintest.ChainSpec{
Expand Down Expand Up @@ -413,11 +423,17 @@ func (p *Chain) SubmitConsumerAdditionProposal(ctx context.Context, chainID stri
if config.ValidatorPowerCap > 0 {
prop.ValidatorsPowerCap = uint32(config.ValidatorPowerCap)
}
if config.AllowInactiveVals {
prop.AllowInactiveVals = true
}
if config.MinStake > 0 {
prop.MinStake = config.MinStake
}
propTx, err := p.ConsumerAdditionProposal(ctx, interchaintest.FaucetAccountKeyName, prop)
if err != nil {
return nil, nil, err
}
errCh := make(chan error)
errCh := make(chan error, 1)
go func() {
defer close(errCh)
if err := p.WaitForProposalStatus(ctx, propTx.ProposalID, govv1.StatusDepositPeriod); err != nil {
Expand Down Expand Up @@ -466,11 +482,7 @@ func (p *Chain) CheckCCV(ctx context.Context, consumer *Chain, relayer *Relayer,
return err
}

_, err = p.Validators[valIdx].ExecTx(ctx, providerAddress.Moniker,
"staking", "delegate",
providerAddress.ValoperAddress, fmt.Sprintf("%d%s", amount, p.Config().Denom),
)
if err != nil {
if err := p.Validators[valIdx].StakingDelegate(ctx, providerAddress.Moniker, providerAddress.ValoperAddress, fmt.Sprintf("%d%s", amount, p.Config().Denom)); err != nil {
return err
}

Expand Down Expand Up @@ -535,26 +547,55 @@ func (p *Chain) IsValoperJailed(ctx context.Context, valoper string) (bool, erro
return gjson.GetBytes(out, "validator.jailed").Bool(), nil
}

func (p *Chain) IsValidatorJailedForConsumerDowntime(ctx context.Context, relayer Relayer, consumer *Chain, validatorIdx int) (jailed bool, err error) {
if err = consumer.Validators[validatorIdx].StopContainer(ctx); err != nil {
func (p *Chain) IsValidatorJailedForConsumerDowntime(ctx context.Context, relayer *Relayer, consumer *Chain, validatorIdx int) (jailed bool, err error) {
if err = consumer.Validators[validatorIdx].PauseContainer(ctx); err != nil {
return
}
defer func() {
err = consumer.Validators[validatorIdx].StartContainer(ctx)
sErr := consumer.Validators[validatorIdx].UnpauseContainer(ctx)
if sErr != nil {
err = multierr.Append(err, sErr)
return
}
time.Sleep(10 * CommitTimeout)
if jailed && err == nil {
if _, err = p.Validators[validatorIdx].ExecTx(ctx, p.ValidatorWallets[validatorIdx].Moniker, "slashing", "unjail"); err != nil {
return
}
var stillJailed bool
if stillJailed, err = p.IsValoperJailed(ctx, p.ValidatorWallets[validatorIdx].ValoperAddress); stillJailed {
err = fmt.Errorf("validator %d is still jailed after unjailing", validatorIdx)
}
}
}()
channel, err := relayer.GetChannelWithPort(ctx, consumer, p, "consumer")
if err != nil {
return
}
if err = testutil.WaitForBlocks(ctx, SlashingWindowConsumer+1, consumer); err != nil {
return
}
rs := relayer.Exec(ctx, GetRelayerExecReporter(ctx), []string{
"hermes", "clear", "packets", "--port", "consumer", "--channel", channel.ChannelID,
"--chain", consumer.Config().ChainID,
}, nil)
if rs.Err != nil {
return false, rs.Err
if p.Config().ChainID != consumer.Config().ChainID {
var channel *ibc.ChannelOutput
channel, err = relayer.GetChannelWithPort(ctx, consumer, p, "consumer")
if err != nil {
return
}
rs := relayer.Exec(ctx, GetRelayerExecReporter(ctx), []string{
"hermes", "clear", "packets", "--port", "consumer", "--channel", channel.ChannelID,
"--chain", consumer.Config().ChainID,
}, nil)
if rs.Err != nil {
return false, rs.Err
}
tCtx, tCancel := context.WithTimeout(ctx, SlashingWindowConsumer*2*CommitTimeout)
defer tCancel()
if err = testutil.WaitForBlocks(tCtx, SlashingWindowConsumer+1, consumer); err != nil {
if tCtx.Err() != nil {
err = fmt.Errorf("chain %s is stopped: %w", consumer.Config().ChainID, err)
}
return
}
rs = relayer.Exec(ctx, GetRelayerExecReporter(ctx), []string{
"hermes", "clear", "packets", "--port", "consumer", "--channel", channel.ChannelID,
"--chain", consumer.Config().ChainID,
}, nil)
if rs.Err != nil {
return false, rs.Err
}
}
tCtx, tCancel := context.WithTimeout(ctx, 30*CommitTimeout)
defer tCancel()
Expand Down
2 changes: 1 addition & 1 deletion tests/interchain/chainsuite/relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func NewRelayer(ctx context.Context, testName interchaintest.TestName) (*Relayer
rly := interchaintest.NewBuiltinRelayerFactory(
ibc.Hermes,
GetLogger(ctx),
relayer.CustomDockerImage("ghcr.io/informalsystems/hermes", "v1.8.0", "1000:1000"),
relayer.CustomDockerImage("ghcr.io/informalsystems/hermes", "1.10.1", "2000:2000"),
).Build(testName, dockerClient, dockerNetwork)
return &Relayer{Relayer: rly}, nil
}
Expand Down
175 changes: 175 additions & 0 deletions tests/interchain/consensus_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package interchain_test

import (
"encoding/json"
"fmt"
"strings"
"testing"

"github.com/cosmos/gaia/v20/tests/interchain/chainsuite"
"github.com/stretchr/testify/suite"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

const (
maxValidators = 5
maxProviderConsensusValidators = 4
)

type ConsensusSuite struct {
*chainsuite.Suite
Consumer *chainsuite.Chain
}

func (s *ConsensusSuite) SetupSuite() {
s.Suite.SetupSuite()
authority, err := s.Chain.GetGovernanceAddress(s.GetContext())
s.Require().NoError(err)

stakingProposal := fmt.Sprintf(`{
"@type": "/cosmos.staking.v1beta1.MsgUpdateParams",
"authority": "%s",
"params": {
"unbonding_time": "1814400s",
"max_validators": 5,
"max_entries": 7,
"historical_entries": 10000,
"bond_denom": "%s",
"min_commission_rate": "0.050000000000000000",
"validator_bond_factor": "250.000000000000000000",
"global_liquid_staking_cap": "0.250000000000000000",
"validator_liquid_staking_cap": "0.500000000000000000"
}
}`, authority, s.Chain.Config().Denom)

prop, err := s.Chain.BuildProposal(nil, "update staking params", "update staking params", "", chainsuite.GovDepositAmount, "", false)
s.Require().NoError(err)
prop.Messages = []json.RawMessage{json.RawMessage(stakingProposal)}
result, err := s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop)
s.Require().NoError(err)
s.Require().NoError(s.Chain.PassProposal(s.GetContext(), result.ProposalID))
s.UpgradeChain()

stakingParams, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "staking", "params")
s.Require().NoError(err)
s.Require().Equal(uint64(200), gjson.GetBytes(stakingParams, "params.max_validators").Uint(), string(stakingParams))

providerParams, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "provider", "params")
s.Require().NoError(err)
s.Require().Equal(uint64(180), gjson.GetBytes(providerParams, "max_provider_consensus_validators").Uint(), string(providerParams))
providerParams, err = sjson.SetBytes(providerParams, "max_provider_consensus_validators", maxProviderConsensusValidators)
s.Require().NoError(err)
providerProposal, err := sjson.SetRaw(fmt.Sprintf(`{
"@type": "/interchain_security.ccv.provider.v1.MsgUpdateParams",
"authority": "%s"
}`, authority), "params", string(providerParams))
s.Require().NoError(err)

stakingProposal, err = sjson.Set(stakingProposal, "params.max_validators", maxValidators)
s.Require().NoError(err)
prop, err = s.Chain.BuildProposal(nil, "update staking params", "update staking params", "", chainsuite.GovDepositAmount, "", false)
s.Require().NoError(err)
prop.Messages = append(prop.Messages, json.RawMessage(stakingProposal))
result, err = s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop)
s.Require().NoError(err)
s.Require().NoError(s.Chain.PassProposal(s.GetContext(), result.ProposalID))

prop, err = s.Chain.BuildProposal(nil, "update provider params", "update provider params", "", chainsuite.GovDepositAmount, "", false)
s.Require().NoError(err)
prop.Messages = []json.RawMessage{json.RawMessage(providerProposal)}
result, err = s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop)
s.Require().NoError(err)
s.Require().NoError(s.Chain.PassProposal(s.GetContext(), result.ProposalID))

cfg := chainsuite.ConsumerConfig{
ChainName: "ics-consumer",
Version: "v5.0.0",
ShouldCopyProviderKey: allProviderKeysCopied(),
Denom: chainsuite.Ucon,
TopN: 100,
AllowInactiveVals: true,
}
consumer, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg)
s.Require().NoError(err)
err = s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)
s.Require().NoError(err)
s.Consumer = consumer
}

// This is called 0ValidatorSets because it should run first; if the validator sets are wrong, obviously the other tests will fail
func (s *ConsensusSuite) Test0ValidatorSets() {
vals, err := s.Chain.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set")
s.Require().NoError(err)
s.Require().Equal(maxProviderConsensusValidators, len(vals.Array()), vals)
for i := 0; i < maxProviderConsensusValidators; i++ {
valCons := vals.Array()[i].Get("address").String()
expectedValCons, _, err := s.Chain.Validators[i].ExecBin(s.GetContext(), "comet", "show-address")
s.Require().NoError(err)
s.Require().Equal(strings.TrimSpace(string(expectedValCons)), valCons)
}

vals, err = s.Consumer.QueryJSON(s.GetContext(), "validators", "comet-validator-set")
s.Require().NoError(err)
s.Require().Equal(maxProviderConsensusValidators, len(vals.Array()), vals)
for i := 0; i < maxProviderConsensusValidators; i++ {
valCons := vals.Array()[i].Get("address").String()
expectedValCons, _, err := s.Consumer.Validators[i].ExecBin(s.GetContext(), "comet", "show-address")
s.Require().NoError(err)
s.Require().Equal(strings.TrimSpace(string(expectedValCons)), valCons)
}
}

func (s *ConsensusSuite) TestProviderJailing() {
for i := 1; i < maxProviderConsensusValidators; i++ {
jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Chain, i)
s.Require().NoError(err)
s.Assert().True(jailed, "validator %d should be jailed", i)
}
for i := maxProviderConsensusValidators; i < chainsuite.ValidatorCount; i++ {
jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Chain, i)
s.Require().NoError(err)
s.Assert().False(jailed, "validator %d should not be jailed", i)
}
}

func (s *ConsensusSuite) TestConsumerJailing() {
for i := 1; i < maxProviderConsensusValidators; i++ {
jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Consumer, i)
s.Require().NoError(err)
s.Assert().True(jailed, "validator %d should be jailed", i)
}
for i := maxProviderConsensusValidators; i < chainsuite.ValidatorCount; i++ {
jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Consumer, i)
s.Require().NoError(err)
s.Assert().False(jailed, "validator %d should not be jailed", i)
}
}

func (s *ConsensusSuite) TestOptInInactive() {
_, err := s.Chain.Validators[4].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[4].Moniker, "provider", "opt-in", s.Consumer.Config().ChainID)
s.Require().NoError(err)
defer func() {
_, err := s.Chain.Validators[4].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[4].Moniker, "provider", "opt-out", s.Consumer.Config().ChainID)
s.Require().NoError(err)
jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Consumer, 4)
s.Require().NoError(err)
s.Assert().False(jailed, "validator 4 should be jailed")
}()
jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Consumer, 4)
s.Require().NoError(err)
s.Assert().True(jailed, "validator 4 should be jailed")

_, err = s.Chain.Validators[5].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[5].Moniker, "provider", "opt-in", s.Consumer.Config().ChainID)
s.Require().Error(err)
jailed, err = s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Consumer, 5)
s.Require().NoError(err)
s.Assert().False(jailed, "validator 5 should not be jailed")
}

func TestConsensus(t *testing.T) {
s := &ConsensusSuite{
Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{CreateRelayer: true}),
}
suite.Run(t, s)
}
8 changes: 4 additions & 4 deletions tests/interchain/consumer_launch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ func (s *ConsumerLaunchSuite) TestChainLaunch() {
s.Require().NoError(err)
s.Require().NoError(chainsuite.SendSimpleIBCTx(s.GetContext(), s.Chain, consumer, s.Relayer))

jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), *s.Relayer, consumer, 1)
jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, consumer, 1)
s.Require().NoError(err)
s.Require().True(jailed, "validator 1 should be jailed for downtime")
jailed, err = s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), *s.Relayer, consumer, 5)
jailed, err = s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, consumer, 5)
s.Require().NoError(err)
s.Require().False(jailed, "validator 5 should not be jailed for downtime")

Expand All @@ -59,10 +59,10 @@ func (s *ConsumerLaunchSuite) TestChainLaunch() {
s.Require().NoError(err)
s.Require().NoError(chainsuite.SendSimpleIBCTx(s.GetContext(), s.Chain, consumer2, s.Relayer))

jailed, err = s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), *s.Relayer, consumer2, 1)
jailed, err = s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, consumer2, 1)
s.Require().NoError(err)
s.Require().True(jailed, "validator 1 should be jailed for downtime")
jailed, err = s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), *s.Relayer, consumer2, 5)
jailed, err = s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, consumer2, 5)
s.Require().NoError(err)
s.Require().False(jailed, "validator 5 should not be jailed for downtime")
}
Expand Down
Loading

0 comments on commit bb96cde

Please sign in to comment.