Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: break up interchaintests into user stories #3405

Merged
merged 3 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/interchain-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
matrix:
${{fromJson(needs.prepare-matrix.outputs.matrix)}}
fail-fast: false
max-parallel: 3
max-parallel: 10
steps:
- name: Check out repository code
uses: actions/checkout@v4
Expand Down
6 changes: 3 additions & 3 deletions tests/interchain/chainsuite/chain_ics.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type ConsumerConfig struct {
ChainName string
Version string
Denom string
ShouldCopyProviderKey [ValidatorCount]bool
ShouldCopyProviderKey []bool
TopN int
ValidatorSetCap int
ValidatorPowerCap int
Expand Down Expand Up @@ -100,8 +100,8 @@ func newProposalWaiter() *proposalWaiter {
func (p *Chain) AddConsumerChain(ctx context.Context, relayer *Relayer, config ConsumerConfig) (*Chain, error) {
dockerClient, dockerNetwork := GetDockerContext(ctx)

if len(config.ShouldCopyProviderKey) != ValidatorCount {
return nil, fmt.Errorf("shouldCopyProviderKey must be the same length as the number of validators")
if len(config.ShouldCopyProviderKey) < len(p.Validators) {
return nil, fmt.Errorf("shouldCopyProviderKey should have at least %d elements", len(p.Validators))
}

spawnTime := time.Now().Add(ChainSpawnWait)
Expand Down
16 changes: 12 additions & 4 deletions tests/interchain/chainsuite/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const (
DowntimeJailDuration = 10 * time.Second
ProviderSlashingWindow = 10
GasPrices = "0.005" + Uatom
ValidatorCount = 6
// ValidatorCount = 1
UpgradeDelta = 30
ValidatorFunds = 11_000_000_000
ChainSpawnWait = 155 * time.Second
Expand All @@ -57,6 +57,12 @@ const (
ICSUidGuid = "1025:1025"
)

// These have to be vars so we can take their address
var (
OneValidator int = 1
SixValidators int = 6
)

func MergeChainSpecs(spec, other *interchaintest.ChainSpec) *interchaintest.ChainSpec {
if spec == nil {
return other
Expand Down Expand Up @@ -96,12 +102,15 @@ func (c SuiteConfig) Merge(other SuiteConfig) SuiteConfig {

func DefaultGenesisAmounts(denom string) func(i int) (types.Coin, types.Coin) {
return func(i int) (types.Coin, types.Coin) {
if i >= SixValidators {
panic("your chain has too many validators")
}
return types.Coin{
Denom: denom,
Amount: sdkmath.NewInt(ValidatorFunds),
}, types.Coin{
Denom: denom,
Amount: sdkmath.NewInt([ValidatorCount]int64{
Amount: sdkmath.NewInt([]int64{
30_000_000,
29_000_000,
20_000_000,
Expand All @@ -115,7 +124,6 @@ func DefaultGenesisAmounts(denom string) func(i int) (types.Coin, types.Coin) {

func DefaultChainSpec(env Environment) *interchaintest.ChainSpec {
fullNodes := 0
validators := ValidatorCount
var repository string
if env.DockerRegistry == "" {
repository = env.GaiaImageName
Expand All @@ -125,7 +133,7 @@ func DefaultChainSpec(env Environment) *interchaintest.ChainSpec {
return &interchaintest.ChainSpec{
Name: "gaia",
NumFullNodes: &fullNodes,
NumValidators: &validators,
NumValidators: &OneValidator,
Version: env.OldGaiaImageVersion,
ChainConfig: ibc.ChainConfig{
Denom: Uatom,
Expand Down
242 changes: 242 additions & 0 deletions tests/interchain/consumer_chain/changeover_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package consumer_chain_test

import (
"context"
"fmt"
"strconv"
"strings"
"testing"
"time"

sdkmath "cosmossdk.io/math"

"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/gaia/v21/tests/interchain/chainsuite"
transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"
"github.com/strangelove-ventures/interchaintest/v8"
"github.com/strangelove-ventures/interchaintest/v8/chain/cosmos"
"github.com/strangelove-ventures/interchaintest/v8/ibc"
"github.com/strangelove-ventures/interchaintest/v8/testutil"
"github.com/stretchr/testify/suite"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
"golang.org/x/sync/errgroup"
)

type ChangeoverSuite struct {
*chainsuite.Suite
consumerCfg chainsuite.ConsumerConfig
Consumer *chainsuite.Chain
}

func (s *ChangeoverSuite) SetupSuite() {
s.Suite.SetupSuite()
validators := 1
fullNodes := 0
genesisChanges := []cosmos.GenesisKV{
cosmos.NewGenesisKV("app_state.gov.params.voting_period", chainsuite.GovVotingPeriod.String()),
cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", chainsuite.GovDepositPeriod.String()),
cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.denom", chainsuite.Ucon),
cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", strconv.Itoa(chainsuite.GovMinDepositAmount)),
}
spec := &interchaintest.ChainSpec{
Name: "ics-consumer",
ChainName: "ics-consumer",
// Unfortunately, this rc is a bit of a bespoke version; it corresponds to an rc
// in hypha's fork that has a fix for sovereign -> consumer changeovers
Version: "v6.2.0-rc1",
NumValidators: &validators,
NumFullNodes: &fullNodes,
ChainConfig: ibc.ChainConfig{
Denom: chainsuite.Ucon,
GasPrices: "0.025" + chainsuite.Ucon,
GasAdjustment: 2.0,
Gas: "auto",
ConfigFileOverrides: map[string]any{
"config/config.toml": chainsuite.DefaultConfigToml(),
},
ModifyGenesisAmounts: chainsuite.DefaultGenesisAmounts(chainsuite.Ucon),
ModifyGenesis: cosmos.ModifyGenesis(genesisChanges),
Bin: "interchain-security-sd",
Images: []ibc.DockerImage{
{
Repository: chainsuite.HyphaICSRepo,
Version: "v6.2.0-rc1",
UidGid: chainsuite.ICSUidGuid,
},
},
Bech32Prefix: "consumer",
},
}
consumer, err := s.Chain.AddLinkedChain(s.GetContext(), s.T(), s.Relayer, spec)
s.Require().NoError(err)

s.Consumer = consumer

s.UpgradeChain()
}

func (s *ChangeoverSuite) TestRewardsWithChangeover() {
transferCh, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, s.Consumer)
s.Require().NoError(err)
rewardDenom := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", transferCh.ChannelID, s.Consumer.Config().Denom)).IBCDenom()

s.Run("changeover", func() {
s.changeSovereignToConsumer(s.Consumer, transferCh)
})

s.Run("rewards", func() {
govAuthority, err := s.Chain.GetGovernanceAddress(s.GetContext())
s.Require().NoError(err)
rewardDenomsProp := providertypes.MsgChangeRewardDenoms{
DenomsToAdd: []string{rewardDenom},
Authority: govAuthority,
}
prop, err := s.Chain.BuildProposal([]cosmos.ProtoMessage{&rewardDenomsProp},
"add denoms to list of registered reward denoms",
"add denoms to list of registered reward denoms",
"", chainsuite.GovDepositAmount, "", false)
s.Require().NoError(err)
propResult, err := s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop)
s.Require().NoError(err)
s.Require().NoError(s.Chain.PassProposal(s.GetContext(), propResult.ProposalID))

faucetAddrBts, err := s.Consumer.GetAddress(s.GetContext(), interchaintest.FaucetAccountKeyName)
s.Require().NoError(err)
faucetAddr := types.MustBech32ifyAddressBytes(s.Consumer.Config().Bech32Prefix, faucetAddrBts)
_, err = s.Consumer.Validators[0].ExecTx(
s.GetContext(), interchaintest.FaucetAccountKeyName,
"bank", "send", string(faucetAddr), s.Consumer.ValidatorWallets[0].Address,
"1"+s.Consumer.Config().Denom, "--fees", "100000000"+s.Consumer.Config().Denom,
)
s.Require().NoError(err)

s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), chainsuite.BlocksPerDistribution+2, s.Chain, s.Consumer))
s.Require().NoError(s.Relayer.ClearTransferChannel(s.GetContext(), s.Chain, s.Consumer))
s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain, s.Consumer))

rewardStr, err := s.Chain.QueryJSON(
s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", rewardDenom),
"distribution", "rewards", s.Chain.ValidatorWallets[0].Address,
)
s.Require().NoError(err)
rewards, err := chainsuite.StrToSDKInt(rewardStr.String())
s.Require().NoError(err)
s.Require().True(rewards.GT(sdkmath.NewInt(0)), "rewards: %s", rewards.String())
})
}

func TestChangeover(t *testing.T) {
s := &ChangeoverSuite{
Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{
CreateRelayer: true,
ChainSpec: &interchaintest.ChainSpec{
NumValidators: &chainsuite.OneValidator,
},
}),
consumerCfg: chainsuite.ConsumerConfig{
ChainName: "ics-consumer",
ShouldCopyProviderKey: allProviderKeysCopied(),
Denom: chainsuite.Ucon,
TopN: 0,
AllowInactiveVals: true,
MinStake: 1_000_000,
},
}
suite.Run(t, s)
}

func (s *ChangeoverSuite) changeSovereignToConsumer(consumer *chainsuite.Chain, transferCh *ibc.ChannelOutput) {
cfg := s.consumerCfg
currentHeight, err := consumer.Height(s.GetContext())
s.Require().NoError(err)
initialHeight := uint64(currentHeight) + 60
cfg.InitialHeight = initialHeight
spawnTime := time.Now().Add(60 * time.Second)
cfg.DistributionTransmissionChannel = transferCh.ChannelID

err = s.Chain.CreateConsumerPermissionless(s.GetContext(), consumer.Config().ChainID, cfg, spawnTime)
s.Require().NoError(err)

consumerChains, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "provider", "list-consumer-chains")
s.Require().NoError(err)
consumerChain := gjson.GetBytes(consumerChains, fmt.Sprintf("chains.#(chain_id=%q)", consumer.Config().ChainID))
consumerID := consumerChain.Get("consumer_id").String()

eg := errgroup.Group{}
for i := range consumer.Validators {
i := i
eg.Go(func() error {
key, _, err := consumer.Validators[i].ExecBin(s.GetContext(), "tendermint", "show-validator")
if err != nil {
return err
}
keyStr := strings.TrimSpace(string(key))
_, err = s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID, keyStr)
return err
})
}
s.Require().NoError(eg.Wait())

s.Require().NoError(err)
time.Sleep(time.Until(spawnTime))
s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain))

proposal := cosmos.SoftwareUpgradeProposal{
Deposit: "5000000" + chainsuite.Ucon,
Title: "Changeover",
Name: "sovereign-changeover",
Description: "Changeover",
Height: int64(initialHeight) - 3,
}
upgradeTx, err := consumer.UpgradeProposal(s.GetContext(), interchaintest.FaucetAccountKeyName, proposal)
s.Require().NoError(err)
err = consumer.PassProposal(s.GetContext(), upgradeTx.ProposalID)
s.Require().NoError(err)

currentHeight, err = consumer.Height(s.GetContext())
s.Require().NoError(err)

timeoutCtx, timeoutCtxCancel := context.WithTimeout(s.GetContext(), (time.Duration(int64(initialHeight)-currentHeight)+10)*chainsuite.CommitTimeout)
defer timeoutCtxCancel()
err = testutil.WaitForBlocks(timeoutCtx, int(int64(initialHeight)-currentHeight)+3, consumer)
s.Require().Error(err)

s.Require().NoError(consumer.StopAllNodes(s.GetContext()))

genesis, err := consumer.GetNode().GenesisFileContent(s.GetContext())
s.Require().NoError(err)

ccvState, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "provider", "consumer-genesis", consumerID)
s.Require().NoError(err)
genesis, err = sjson.SetRawBytes(genesis, "app_state.ccvconsumer", ccvState)
s.Require().NoError(err)

genesis, err = sjson.SetBytes(genesis, "app_state.slashing.params.signed_blocks_window", strconv.Itoa(chainsuite.SlashingWindowConsumer))
s.Require().NoError(err)
genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.reward_denoms", []string{chainsuite.Ucon})
s.Require().NoError(err)
genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.provider_reward_denoms", []string{s.Chain.Config().Denom})
s.Require().NoError(err)
genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.blocks_per_distribution_transmission", chainsuite.BlocksPerDistribution)
s.Require().NoError(err)

for _, val := range consumer.Validators {
val := val
eg.Go(func() error {
if err := val.OverwriteGenesisFile(s.GetContext(), []byte(genesis)); err != nil {
return err
}
return val.WriteFile(s.GetContext(), []byte(genesis), ".sovereign/config/genesis.json")
})
}
s.Require().NoError(eg.Wait())

consumer.ChangeBinary(s.GetContext(), "interchain-security-cdd")
s.Require().NoError(consumer.StartAllNodes(s.GetContext()))
s.Require().NoError(s.Relayer.ConnectProviderConsumer(s.GetContext(), s.Chain, consumer))
s.Require().NoError(s.Relayer.StopRelayer(s.GetContext(), chainsuite.GetRelayerExecReporter(s.GetContext())))
s.Require().NoError(s.Relayer.StartRelayer(s.GetContext(), chainsuite.GetRelayerExecReporter(s.GetContext())))
s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1))
}
Loading
Loading