Skip to content

Commit

Permalink
WIP test for consumer proposal migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
fastfadingviolets committed Sep 10, 2024
1 parent f7effb7 commit 98ffeb0
Show file tree
Hide file tree
Showing 10 changed files with 576 additions and 68 deletions.
3 changes: 2 additions & 1 deletion tests/interchain/chainsuite/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ func (c *Chain) WaitForProposalStatus(ctx context.Context, proposalID string, st
if err != nil {
return err
}
maxHeight := chainHeight + UpgradeDelta
// At 4s per block, 75 blocks is about 5 minutes.
maxHeight := chainHeight + 75
_, err = cosmos.PollForProposalStatusV1(ctx, c.CosmosChain, chainHeight, maxHeight, uint64(propID), status)
return err
}
Expand Down
209 changes: 170 additions & 39 deletions tests/interchain/chainsuite/chain_ics.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package chainsuite

import (
"context"
"encoding/json"
"fmt"
"path"
"strconv"
"time"

Expand All @@ -21,6 +23,7 @@ import (
sdkmath "cosmossdk.io/math"

govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"
)

type ConsumerBootstrapCb func(ctx context.Context, consumer *cosmos.CosmosChain)
Expand Down Expand Up @@ -101,9 +104,21 @@ func (p *Chain) AddConsumerChain(ctx context.Context, relayer *Relayer, config C
spawnTime := time.Now().Add(ChainSpawnWait)
chainID := fmt.Sprintf("%s-%d", config.ChainName, len(p.Consumers)+1)

proposalWaiter, errCh, err := p.SubmitConsumerAdditionProposal(ctx, chainID, config, spawnTime)
if err != nil {
return nil, err
var proposalWaiter *proposalWaiter
var errCh chan error
if p.GetNode().HasCommand(ctx, "tx", "provider", "create-consumer") {
errCh = make(chan error, 1)
close(errCh)
err := p.CreateConsumerPermissionless(ctx, chainID, config, spawnTime)
if err != nil {
return nil, err
}
} else {
var err error
proposalWaiter, errCh, err = p.SubmitConsumerAdditionProposal(ctx, chainID, config, spawnTime)
if err != nil {
return nil, err
}
}

if config.spec == nil {
Expand Down Expand Up @@ -193,6 +208,113 @@ func (p *Chain) AddConsumerChain(ctx context.Context, relayer *Relayer, config C
return consumer, nil
}

func (p *Chain) CreateConsumerPermissionless(ctx context.Context, chainID string, config ConsumerConfig, spawnTime time.Time) error {
initParams := &providertypes.ConsumerInitializationParameters{
InitialHeight: clienttypes.Height{RevisionNumber: clienttypes.ParseChainID(chainID), RevisionHeight: 1},
SpawnTime: spawnTime,
BlocksPerDistributionTransmission: 1000,
CcvTimeoutPeriod: 2419200000000000,
TransferTimeoutPeriod: 3600000000000,
ConsumerRedistributionFraction: "0.75",
HistoricalEntries: 10000,
UnbondingPeriod: 1728000000000000,
GenesisHash: []byte("Z2VuX2hhc2g="),
BinaryHash: []byte("YmluX2hhc2g="),
}
powerShapingParams := &providertypes.PowerShapingParameters{
Top_N: 0,
ValidatorSetCap: uint32(config.ValidatorSetCap),
ValidatorsPowerCap: uint32(config.ValidatorPowerCap),
AllowInactiveVals: config.AllowInactiveVals,
MinStake: config.MinStake,
}
params := providertypes.MsgCreateConsumer{
ChainId: chainID,
Metadata: providertypes.ConsumerMetadata{
Name: config.ChainName,
Description: "Consumer chain",
Metadata: "ipfs://",
},
InitializationParameters: initParams,
PowerShapingParameters: powerShapingParams,
}

paramsBz, err := json.Marshal(params)
if err != nil {
return err
}
err = p.GetNode().WriteFile(ctx, paramsBz, "consumer-addition.json")
if err != nil {
return err
}
_, err = p.GetNode().ExecTx(ctx, interchaintest.FaucetAccountKeyName, "provider", "create-consumer", path.Join(p.GetNode().HomeDir(), "consumer-addition.json"))
if err != nil {
return err
}
if config.TopN >= 0 {
govAddress, err := p.GetGovernanceAddress(ctx)
if err != nil {
return err
}
consumerID, err := p.QueryJSON(ctx, fmt.Sprintf("chains.#(chain_id=%q).consumer_id", chainID), "provider", "list-consumer-chains")
if err != nil {
return err
}
update := &providertypes.MsgUpdateConsumer{
ConsumerId: consumerID.String(),
NewOwnerAddress: govAddress,
Metadata: &providertypes.ConsumerMetadata{
Name: config.ChainName,
Description: "Consumer chain",
Metadata: "ipfs://",
},
InitializationParameters: initParams,
PowerShapingParameters: powerShapingParams,
}
updateBz, err := json.Marshal(update)
if err != nil {
return err
}
err = p.GetNode().WriteFile(ctx, updateBz, "consumer-update.json")
if err != nil {
return err
}
_, err = p.GetNode().ExecTx(ctx, interchaintest.FaucetAccountKeyName, "provider", "update-consumer", path.Join(p.GetNode().HomeDir(), "consumer-update.json"))
if err != nil {
return err
}
powerShapingParams.Top_N = uint32(config.TopN)
update = &providertypes.MsgUpdateConsumer{
Owner: govAddress,
ConsumerId: consumerID.String(),
Metadata: &providertypes.ConsumerMetadata{
Name: config.ChainName,
Description: "Consumer chain",
Metadata: "ipfs://",
},
InitializationParameters: initParams,
PowerShapingParameters: powerShapingParams,
}
prop, err := p.BuildProposal([]cosmos.ProtoMessage{update}, "update consumer", "update consumer", "", GovDepositAmount, "", false)
if err != nil {
return err
}
txhash, err := p.GetNode().SubmitProposal(ctx, p.ValidatorWallets[0].Moniker, prop)
if err != nil {
return err
}
propID, err := p.GetProposalID(ctx, txhash)
if err != nil {
return err
}
if err := p.PassProposal(ctx, propID); err != nil {
return err
}

}
return nil
}

func (p *Chain) DefaultConsumerChainSpec(ctx context.Context, chainID string, config ConsumerConfig, spawnTime time.Time, proposalWaiter *proposalWaiter) *interchaintest.ChainSpec {
const (
strideChain = "stride"
Expand Down Expand Up @@ -281,13 +403,17 @@ func (p *Chain) DefaultConsumerChainSpec(ctx context.Context, chainID string, co
if config.DuringDepositPeriod != nil {
config.DuringDepositPeriod(ctx, consumer.(*cosmos.CosmosChain))
}
proposalWaiter.AllowDeposit()
proposalWaiter.WaitForVotingPeriod()
if proposalWaiter != nil {
proposalWaiter.AllowDeposit()
proposalWaiter.WaitForVotingPeriod()
}
if config.DuringVotingPeriod != nil {
config.DuringVotingPeriod(ctx, consumer.(*cosmos.CosmosChain))
}
proposalWaiter.AllowVote()
proposalWaiter.WaitForPassed()
if proposalWaiter != nil {
proposalWaiter.AllowVote()
proposalWaiter.WaitForPassed()
}
tCtx, tCancel := context.WithDeadline(ctx, spawnTime)
defer tCancel()
if config.BeforeSpawnTime != nil {
Expand Down Expand Up @@ -397,6 +523,42 @@ func connectProviderConsumer(ctx context.Context, provider *Chain, consumer *Cha

func (p *Chain) SubmitConsumerAdditionProposal(ctx context.Context, chainID string, config ConsumerConfig, spawnTime time.Time) (*proposalWaiter, chan error, error) {
propWaiter := newProposalWaiter()
prop := p.buildConsumerAdditionJSON(chainID, config, spawnTime)
propTx, err := p.ConsumerAdditionProposal(ctx, interchaintest.FaucetAccountKeyName, prop)
if err != nil {
return nil, nil, err
}
errCh := make(chan error, 1)
go func() {
defer close(errCh)
if err := p.WaitForProposalStatus(ctx, propTx.ProposalID, govv1.StatusDepositPeriod); err != nil {
errCh <- err
panic(err)
}
propWaiter.waitForDepositAllowed()

if _, err := p.GetNode().ExecTx(ctx, interchaintest.FaucetAccountKeyName, "gov", "deposit", propTx.ProposalID, prop.Deposit); err != nil {
errCh <- err
panic(err)
}

if err := p.WaitForProposalStatus(ctx, propTx.ProposalID, govv1.StatusVotingPeriod); err != nil {
errCh <- err
panic(err)
}
propWaiter.startVotingPeriod()
propWaiter.waitForVoteAllowed()

if err := p.PassProposal(ctx, propTx.ProposalID); err != nil {
errCh <- err
panic(err)
}
propWaiter.pass()
}()
return propWaiter, errCh, nil
}

func (p *Chain) buildConsumerAdditionJSON(chainID string, config ConsumerConfig, spawnTime time.Time) ccvclient.ConsumerAdditionProposalJSON {
prop := ccvclient.ConsumerAdditionProposalJSON{
Title: fmt.Sprintf("Addition of %s consumer chain", chainID),
Summary: "Proposal to add new consumer chain",
Expand Down Expand Up @@ -429,38 +591,7 @@ func (p *Chain) SubmitConsumerAdditionProposal(ctx context.Context, chainID stri
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, 1)
go func() {
defer close(errCh)
if err := p.WaitForProposalStatus(ctx, propTx.ProposalID, govv1.StatusDepositPeriod); err != nil {
errCh <- err
return
}
propWaiter.waitForDepositAllowed()

if _, err := p.GetNode().ExecTx(ctx, interchaintest.FaucetAccountKeyName, "gov", "deposit", propTx.ProposalID, prop.Deposit); err != nil {
errCh <- err
return
}

if err := p.WaitForProposalStatus(ctx, propTx.ProposalID, govv1.StatusVotingPeriod); err != nil {
errCh <- err
return
}
propWaiter.startVotingPeriod()
propWaiter.waitForVoteAllowed()

if err := p.PassProposal(ctx, propTx.ProposalID); err != nil {
errCh <- err
return
}
propWaiter.pass()
}()
return propWaiter, errCh, nil
return prop
}

func (p *Chain) CheckCCV(ctx context.Context, consumer *Chain, relayer *Relayer, amount, valIdx, blocksPerEpoch int) error {
Expand Down
9 changes: 9 additions & 0 deletions tests/interchain/chainsuite/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@ import (
"github.com/cosmos/cosmos-sdk/types"
)

type ChainScope int

const (
ChainScopeSuite ChainScope = iota
ChainScopeTest ChainScope = iota
)

type SuiteConfig struct {
ChainSpec *interchaintest.ChainSpec
UpgradeOnSetup bool
CreateRelayer bool
Scope ChainScope
}

const (
Expand Down Expand Up @@ -68,6 +76,7 @@ func (c SuiteConfig) Merge(other SuiteConfig) SuiteConfig {
}
c.UpgradeOnSetup = other.UpgradeOnSetup
c.CreateRelayer = other.CreateRelayer
c.Scope = other.Scope
return c
}

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", "1.10.1", "2000:2000"),
relayer.CustomDockerImage("ghcr.io/informalsystems/hermes", "1.10.3", "2000:2000"),
).Build(testName, dockerClient, dockerNetwork)
return &Relayer{Relayer: rly}, nil
}
Expand Down
14 changes: 13 additions & 1 deletion tests/interchain/chainsuite/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func NewSuite(config SuiteConfig) *Suite {
return &Suite{Config: newCfg, Env: env}
}

func (s *Suite) SetupSuite() {
func (s *Suite) createChain() {
ctx, err := NewSuiteContext(&s.Suite)
s.Require().NoError(err)
s.ctx = ctx
Expand All @@ -40,6 +40,18 @@ func (s *Suite) SetupSuite() {
}
}

func (s *Suite) SetupTest() {
if s.Config.Scope == ChainScopeTest {
s.createChain()
}
}

func (s *Suite) SetupSuite() {
if s.Config.Scope == ChainScopeSuite {
s.createChain()
}
}

func (s *Suite) GetContext() context.Context {
s.Require().NotNil(s.ctx, "Tried to GetContext before it was set. SetupSuite must run first")
return s.ctx
Expand Down
18 changes: 15 additions & 3 deletions tests/interchain/consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ func (s *ConsensusSuite) TestConsumerJailing() {
s.Require().NoError(err)
s.Assert().True(jailed, "validator %d should be jailed", i)
}
// Validator 4 will have been opted in automatically when the other ones went down
_, err := s.Chain.Validators[maxProviderConsensusValidators].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[maxProviderConsensusValidators].Moniker, "provider", "opt-out", s.getConsumerID())
s.Require().NoError(err)
for i := maxProviderConsensusValidators; i < chainsuite.ValidatorCount; i++ {
jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Consumer, i)
s.Require().NoError(err)
Expand All @@ -146,10 +149,12 @@ func (s *ConsensusSuite) TestConsumerJailing() {
}

func (s *ConsensusSuite) TestOptInInactive() {
_, err := s.Chain.Validators[4].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[4].Moniker, "provider", "opt-in", s.Consumer.Config().ChainID)
consumerID := s.getConsumerID()

_, err := s.Chain.Validators[4].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[4].Moniker, "provider", "opt-in", consumerID)
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)
_, err := s.Chain.Validators[4].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[4].Moniker, "provider", "opt-out", consumerID)
s.Require().NoError(err)
s.Relayer.ClearCCVChannel(s.GetContext(), s.Chain, s.Consumer)
s.Require().EventuallyWithT(func(c *assert.CollectT) {
Expand All @@ -171,7 +176,7 @@ func (s *ConsensusSuite) TestOptInInactive() {
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)
_, err = s.Chain.Validators[5].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[5].Moniker, "provider", "opt-in", consumerID)
s.Require().NoError(err)
s.Require().NoError(s.Relayer.ClearCCVChannel(s.GetContext(), s.Chain, s.Consumer))
vals, err := s.Consumer.QueryJSON(s.GetContext(), "validators", "comet-validator-set")
Expand All @@ -182,6 +187,13 @@ func (s *ConsensusSuite) TestOptInInactive() {
s.Assert().False(jailed, "validator 5 should not be jailed")
}

func (s *ConsensusSuite) getConsumerID() string {
consumerIDJSON, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q).consumer_id", s.Consumer.Config().ChainID), "provider", "list-consumer-chains")
s.Require().NoError(err)
consumerID := consumerIDJSON.String()
return consumerID
}

func TestConsensus(t *testing.T) {
s := &ConsensusSuite{
Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{CreateRelayer: true}),
Expand Down
Loading

0 comments on commit 98ffeb0

Please sign in to comment.