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: Added tests to provider-consumer suite (backport #2456) #2470

Closed
wants to merge 1 commit into from
Closed
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
26 changes: 26 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,29 @@ jobs:
if: env.GIT_DIFF
run: |
make verify-models

test-interchain:
runs-on: Gaia-Runner-medium
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22"
check-latest: true
cache: true
cache-dependency-path: go.sum
- uses: technote-space/[email protected]
id: git_diff
with:
PATTERNS: |
**/*.go
go.mod
go.sum
**/go.mod
**/go.sum
**/Makefile
Makefile
- name: interchain tests
if: env.GIT_DIFF
run: |
make test-interchain
15 changes: 8 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,18 @@ test-integration-cov:
go test ./tests/integration/... -timeout 30m -coverpkg=./... -coverprofile=integration-profile.out -covermode=atomic

# run interchain tests
# we can use PROVIDER_IMAGE_TAG, PROVIDER_IMAGE_NAME, SOUVEREIGN_IMAGE_TAG, and SOUVEREIGN_IMAGE_NAME to run tests with desired docker images,
# including locally built ones that, for example, contain some of our changes that are not yet on the main branch.
# if not provided, default value for PROVIDER_IMAGE_TAG and SOUVEREIGN_IMAGE_TAG is "latest" and for PROVIDER_IMAGE_NAME
# and SOUVEREIGN_IMAGE_NAME is "ghcr.io/cosmos/interchain-security"
# we can use PROVIDER_IMAGE_TAG, PROVIDER_IMAGE_NAME, CONSUMER_IMAGE_TAG, CONSUMER_IMAGE_NAME, SOVEREIGN_IMAGE_TAG, and SOVEREIGN_IMAGE_NAME to run
# tests with desired docker images, including locally built ones that, for example, contain some of our changes that are not yet on the main branch.
# if not provided, default value for image tag is "latest" and for image name is "ghcr.io/cosmos/interchain-security"
test-interchain:
cd tests/interchain && \
PROVIDER_IMAGE_NAME=$(PROVIDER_IMAGE_NAME) \
PROVIDER_IMAGE_TAG=$(PROVIDER_IMAGE_TAG) \
SOUVEREIGN_IMAGE_NAME=$(SOUVEREIGN_IMAGE_NAME) \
SOUVEREIGN_IMAGE_TAG=$(SOUVEREIGN_IMAGE_TAG) \
go test ./... -timeout 30m
SOVEREIGN_IMAGE_NAME=$(SOVEREIGN_IMAGE_NAME) \
SOVEREIGN_IMAGE_TAG=$(SOVEREIGN_IMAGE_TAG) \
CONSUMER_IMAGE_NAME=$(CONSUMER_IMAGE_NAME) \
CONSUMER_IMAGE_TAG=$(CONSUMER_IMAGE_TAG) \
go test ./... -timeout 30m -v

# run mbt tests
test-mbt:
Expand Down
4 changes: 3 additions & 1 deletion TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ make sim-full-no-inactive-vals
#run interchain tests (running with the latest image ghcr.io/cosmos/interchain-security:latest)
make test-interchain
# run interchain tests with specific image(e.g. test-image:local)
make test-interchain PROVIDER_IMAGE_NAME=test-image PROVIDER_IMAGE_TAG=local SOUVEREIGN_IMAGE_NAME=test-image SOUVEREIGN_IMAGE_TAG=local
make test-interchain PROVIDER_IMAGE_NAME=test-image PROVIDER_IMAGE_TAG=local SOVEREIGN_IMAGE_NAME=test-image SOVEREIGN_IMAGE_TAG=local
# to run single interchain test, first navigate to /tests/interchain directory and run the command for desired test e.g.
# go test -run ^TestMultiValidatorProviderSuite/TestOptInChainCanOnlyStartIfActiveValidatorOptedIn$ ./...
```

Alternatively you can run tests using `go test`:
Expand Down
116 changes: 114 additions & 2 deletions tests/interchain/chainsuite/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type Chain struct {
ValidatorWallets []ValidatorWallet
RelayerWallet ibc.Wallet
TestWallets []ibc.Wallet
walletMtx sync.Mutex
walletsInUse map[int]bool
}

type ValidatorWallet struct {
Expand All @@ -49,6 +51,7 @@ func chainFromCosmosChain(cosmos *cosmos.CosmosChain, relayerWallet ibc.Wallet,
c.ValidatorWallets = wallets
c.RelayerWallet = relayerWallet
c.TestWallets = testWallets
c.walletsInUse = make(map[int]bool)
return c, nil
}

Expand Down Expand Up @@ -88,7 +91,7 @@ func CreateChain(ctx context.Context, testName interchaintest.TestName, spec *in
}

// build test wallets
testWallets, err := setupTestWallets(ctx, cosmosChain, TestWalletsNumber)
testWallets, err := SetupTestWallets(ctx, cosmosChain, TestWalletsNumber)
if err != nil {
return nil, err
}
Expand All @@ -100,7 +103,7 @@ func CreateChain(ctx context.Context, testName interchaintest.TestName, spec *in
return chain, nil
}

func setupTestWallets(ctx context.Context, cosmosChain *cosmos.CosmosChain, walletCount int) ([]ibc.Wallet, error) {
func SetupTestWallets(ctx context.Context, cosmosChain *cosmos.CosmosChain, walletCount int) ([]ibc.Wallet, error) {
wallets := make([]ibc.Wallet, walletCount)
eg := new(errgroup.Group)
for i := 0; i < walletCount; i++ {
Expand Down Expand Up @@ -159,6 +162,85 @@ func getValidatorWallets(ctx context.Context, chain *Chain) ([]ValidatorWallet,
return wallets, nil
}

func (p *Chain) AddConsumerChain(ctx context.Context, relayer *Relayer, spec *interchaintest.ChainSpec) (*Chain, error) {
dockerClient, dockerNetwork := GetDockerContext(ctx)

cf := interchaintest.NewBuiltinChainFactory(
GetLogger(ctx),
[]*interchaintest.ChainSpec{spec},
)

chains, err := cf.Chains(p.GetNode().TestName)
if err != nil {
return nil, err
}
consumer := chains[0].(*cosmos.CosmosChain)

// We can't use AddProviderConsumerLink here because the provider chain is already built; we'll have to do everything by hand.
p.Consumers = append(p.Consumers, consumer)
consumer.Provider = p.CosmosChain
relayerWallet, err := consumer.BuildRelayerWallet(ctx, "relayer-"+consumer.Config().ChainID)
if err != nil {
return nil, err
}
wallets := make([]ibc.Wallet, len(p.Validators)+1)
wallets[0] = relayerWallet
// This is a hack, but we need to create wallets for the validators that have the right moniker.
for i := 1; i <= len(p.Validators); i++ {
wallets[i], err = consumer.BuildRelayerWallet(ctx, ValidatorMoniker)
if err != nil {
return nil, err
}
}
walletAmounts := make([]ibc.WalletAmount, len(wallets))
for i, wallet := range wallets {
walletAmounts[i] = ibc.WalletAmount{
Address: wallet.FormattedAddress(),
Denom: consumer.Config().Denom,
Amount: sdkmath.NewInt(TotalValidatorFunds),
}
}

ic := interchaintest.NewInterchain().
AddChain(consumer, walletAmounts...).
AddRelayer(relayer, "relayer")

if err := ic.Build(ctx, GetRelayerExecReporter(ctx), interchaintest.InterchainBuildOptions{
Client: dockerClient,
NetworkID: dockerNetwork,
TestName: p.GetNode().TestName,
}); err != nil {
return nil, err
}

for i, val := range consumer.Validators {
if err := val.RecoverKey(ctx, ValidatorMoniker, wallets[i+1].Mnemonic()); err != nil {
return nil, err
}
}
consumerChain, err := chainFromCosmosChain(consumer, relayerWallet, p.TestWallets)
if err != nil {
return nil, err
}

return consumerChain, nil
}

// GetUnusedTestingAddresss retrieves an unused wallet address and its key name safely
func (p *Chain) GetUnusedTestingAddresss() (formattedAddress string, keyName string, err error) {
p.walletMtx.Lock()
defer p.walletMtx.Unlock()

for i, wallet := range p.TestWallets {
if !p.walletsInUse[i] {
p.walletsInUse[i] = true
return wallet.FormattedAddress(), wallet.KeyName(), nil
}
}

return "", "", fmt.Errorf("no unused wallets available")
}

// UpdateAndVerifyStakeChange updates the staking amount on the provider chain and verifies that the change is reflected on the consumer side
func (p *Chain) UpdateAndVerifyStakeChange(ctx context.Context, consumer *Chain, relayer *Relayer, amount, valIdx int) error {

Expand Down Expand Up @@ -552,6 +634,36 @@ func (c *Chain) GetCcvConsumerParams(ctx context.Context) (ConsumerParamsRespons
return queryResponse, nil
}

func (c *Chain) GetProviderInfo(ctx context.Context) (ProviderInfoResponse, error) {
queryRes, _, err := c.GetNode().ExecQuery(
ctx,
"ccvconsumer", "provider-info",
)
if err != nil {
return ProviderInfoResponse{}, err
}

var queryResponse ProviderInfoResponse
err = json.Unmarshal([]byte(queryRes), &queryResponse)
if err != nil {
return ProviderInfoResponse{}, err
}

return queryResponse, nil
}

func (c *Chain) QueryJSON(ctx context.Context, jsonPath string, query ...string) (gjson.Result, error) {
stdout, _, err := c.GetNode().ExecQuery(ctx, query...)
if err != nil {
return gjson.Result{}, err
}
retval := gjson.GetBytes(stdout, jsonPath)
if !retval.Exists() {
return gjson.Result{}, fmt.Errorf("json path %s not found in query result %s", jsonPath, stdout)
}
return retval, nil
}

func getEvtAttribute(events []abci.Event, evtType string, key string) (string, bool) {
for _, evt := range events {
if evt.GetType() == evtType {
Expand Down
103 changes: 103 additions & 0 deletions tests/interchain/chainsuite/chain_spec_consumer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package chainsuite

import (
"context"
"errors"
"strconv"
"time"

providertypes "github.com/cosmos/interchain-security/v6/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"
)

func GetConsumerSpec(ctx context.Context, providerChain *Chain, proposalMsg *providertypes.MsgCreateConsumer) *interchaintest.ChainSpec {
fullNodes := FullNodeCount
validators := 1

return &interchaintest.ChainSpec{
ChainName: ConsumerChainID,
NumFullNodes: &fullNodes,
NumValidators: &validators,
ChainConfig: ibc.ChainConfig{
ChainID: ConsumerChainID,
Bin: ConsumerBin,
Denom: Stake,
Type: CosmosChainType,
GasPrices: GasPrices + Stake,
GasAdjustment: 2.0,
TrustingPeriod: "336h",
CoinType: "118",
Images: []ibc.DockerImage{
{
Repository: ConsumerImageName(),
Version: ConsumerImageVersion(),
UIDGID: "1025:1025",
},
},
ConfigFileOverrides: map[string]any{
"config/config.toml": DefaultConfigToml(),
},
PreGenesis: func(consumer ibc.Chain) error {
// note that if Top_N>0 proposal will be rejected. If there is a need to support this in the future,
// it is necessary to first submit a create message with Top_N=0 and then an update message with Top_N>0
consumerID, err := providerChain.CreateConsumer(ctx, proposalMsg, ValidatorMoniker)
if err != nil {
return err
}

for index := 0; index < len(providerChain.Validators); index++ {
if err := providerChain.OptIn(ctx, consumerID, index); err != nil {
return err
}
}

// speed up chain launch by submitting update msg with current time as a spawn time
proposalMsg.InitializationParameters.SpawnTime = time.Now()
updateMsg := &providertypes.MsgUpdateConsumer{
ConsumerId: consumerID,
Owner: providerChain.ValidatorWallets[0].Address,
NewOwnerAddress: providerChain.ValidatorWallets[0].Address,
InitializationParameters: proposalMsg.InitializationParameters,
PowerShapingParameters: proposalMsg.PowerShapingParameters,
}
if err := providerChain.UpdateConsumer(ctx, updateMsg, ValidatorMoniker); err != nil {
return err
}

if err := testutil.WaitForBlocks(ctx, 2, providerChain); err != nil {
return err
}

consumerChain, err := providerChain.GetConsumerChainByChainId(ctx, proposalMsg.ChainId)
if err != nil {
return err
}

if consumerChain.Phase != providertypes.CONSUMER_PHASE_LAUNCHED.String() {
return errors.New("consumer chain is not launched")
}

return nil
},
Bech32Prefix: Bech32PrefixConsumer,
ModifyGenesisAmounts: DefaultGenesisAmounts(Stake),
ModifyGenesis: cosmos.ModifyGenesis(consumerModifiedGenesis()),
InterchainSecurityConfig: ibc.ICSConfig{
ConsumerCopyProviderKey: func(int) bool { return true },
},
},
}
}

func consumerModifiedGenesis() []cosmos.GenesisKV {
return []cosmos.GenesisKV{
cosmos.NewGenesisKV("app_state.slashing.params.signed_blocks_window", strconv.Itoa(SlashingWindowConsumer)),
cosmos.NewGenesisKV("consensus.params.block.max_gas", "50000000"),
cosmos.NewGenesisKV("app_state.ccvconsumer.params.soft_opt_out_threshold", "0.0"),
cosmos.NewGenesisKV("app_state.ccvconsumer.params.blocks_per_distribution_transmission", BlocksPerDistribution),
cosmos.NewGenesisKV("app_state.ccvconsumer.params.reward_denoms", []string{Stake}),
}
}
Loading
Loading