Skip to content

Commit 56d788c

Browse files
authored
ci: Added tests to provider-consumer suite (#2456)
* Add provider-consumer interchain tests * remove interchain framework logs - leave only status * add description how to run single interchain test * triger interchain tests * triger interchain tests * remove log filtering * set preccv to true for changeover genesis
1 parent 9563c21 commit 56d788c

13 files changed

+478
-152
lines changed

.github/workflows/test.yml

+26
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,29 @@ jobs:
208208
if: env.GIT_DIFF
209209
run: |
210210
make verify-models
211+
212+
test-interchain:
213+
runs-on: Gaia-Runner-medium
214+
steps:
215+
- uses: actions/checkout@v4
216+
- uses: actions/setup-go@v5
217+
with:
218+
go-version: "1.22"
219+
check-latest: true
220+
cache: true
221+
cache-dependency-path: go.sum
222+
- uses: technote-space/[email protected]
223+
id: git_diff
224+
with:
225+
PATTERNS: |
226+
**/*.go
227+
go.mod
228+
go.sum
229+
**/go.mod
230+
**/go.sum
231+
**/Makefile
232+
Makefile
233+
- name: interchain tests
234+
if: env.GIT_DIFF
235+
run: |
236+
make test-interchain

Makefile

+8-7
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,18 @@ test-integration-cov:
5353
go test ./tests/integration/... -timeout 30m -coverpkg=./... -coverprofile=integration-profile.out -covermode=atomic
5454

5555
# run interchain tests
56-
# we can use PROVIDER_IMAGE_TAG, PROVIDER_IMAGE_NAME, SOUVEREIGN_IMAGE_TAG, and SOUVEREIGN_IMAGE_NAME to run tests with desired docker images,
57-
# including locally built ones that, for example, contain some of our changes that are not yet on the main branch.
58-
# if not provided, default value for PROVIDER_IMAGE_TAG and SOUVEREIGN_IMAGE_TAG is "latest" and for PROVIDER_IMAGE_NAME
59-
# and SOUVEREIGN_IMAGE_NAME is "ghcr.io/cosmos/interchain-security"
56+
# we can use PROVIDER_IMAGE_TAG, PROVIDER_IMAGE_NAME, CONSUMER_IMAGE_TAG, CONSUMER_IMAGE_NAME, SOVEREIGN_IMAGE_TAG, and SOVEREIGN_IMAGE_NAME to run
57+
# 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.
58+
# if not provided, default value for image tag is "latest" and for image name is "ghcr.io/cosmos/interchain-security"
6059
test-interchain:
6160
cd tests/interchain && \
6261
PROVIDER_IMAGE_NAME=$(PROVIDER_IMAGE_NAME) \
6362
PROVIDER_IMAGE_TAG=$(PROVIDER_IMAGE_TAG) \
64-
SOUVEREIGN_IMAGE_NAME=$(SOUVEREIGN_IMAGE_NAME) \
65-
SOUVEREIGN_IMAGE_TAG=$(SOUVEREIGN_IMAGE_TAG) \
66-
go test ./... -timeout 30m
63+
SOVEREIGN_IMAGE_NAME=$(SOVEREIGN_IMAGE_NAME) \
64+
SOVEREIGN_IMAGE_TAG=$(SOVEREIGN_IMAGE_TAG) \
65+
CONSUMER_IMAGE_NAME=$(CONSUMER_IMAGE_NAME) \
66+
CONSUMER_IMAGE_TAG=$(CONSUMER_IMAGE_TAG) \
67+
go test ./... -timeout 30m -v
6768

6869
# run mbt tests
6970
test-mbt:

TESTING.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ make sim-full-no-inactive-vals
9191
#run interchain tests (running with the latest image ghcr.io/cosmos/interchain-security:latest)
9292
make test-interchain
9393
# run interchain tests with specific image(e.g. test-image:local)
94-
make test-interchain PROVIDER_IMAGE_NAME=test-image PROVIDER_IMAGE_TAG=local SOUVEREIGN_IMAGE_NAME=test-image SOUVEREIGN_IMAGE_TAG=local
94+
make test-interchain PROVIDER_IMAGE_NAME=test-image PROVIDER_IMAGE_TAG=local SOVEREIGN_IMAGE_NAME=test-image SOVEREIGN_IMAGE_TAG=local
95+
# to run single interchain test, first navigate to /tests/interchain directory and run the command for desired test e.g.
96+
# go test -run ^TestMultiValidatorProviderSuite/TestOptInChainCanOnlyStartIfActiveValidatorOptedIn$ ./...
9597
```
9698

9799
Alternatively you can run tests using `go test`:

tests/interchain/chainsuite/chain.go

+114-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ type Chain struct {
3232
ValidatorWallets []ValidatorWallet
3333
RelayerWallet ibc.Wallet
3434
TestWallets []ibc.Wallet
35+
walletMtx sync.Mutex
36+
walletsInUse map[int]bool
3537
}
3638

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

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

9093
// build test wallets
91-
testWallets, err := setupTestWallets(ctx, cosmosChain, TestWalletsNumber)
94+
testWallets, err := SetupTestWallets(ctx, cosmosChain, TestWalletsNumber)
9295
if err != nil {
9396
return nil, err
9497
}
@@ -100,7 +103,7 @@ func CreateChain(ctx context.Context, testName interchaintest.TestName, spec *in
100103
return chain, nil
101104
}
102105

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

165+
func (p *Chain) AddConsumerChain(ctx context.Context, relayer *Relayer, spec *interchaintest.ChainSpec) (*Chain, error) {
166+
dockerClient, dockerNetwork := GetDockerContext(ctx)
167+
168+
cf := interchaintest.NewBuiltinChainFactory(
169+
GetLogger(ctx),
170+
[]*interchaintest.ChainSpec{spec},
171+
)
172+
173+
chains, err := cf.Chains(p.GetNode().TestName)
174+
if err != nil {
175+
return nil, err
176+
}
177+
consumer := chains[0].(*cosmos.CosmosChain)
178+
179+
// We can't use AddProviderConsumerLink here because the provider chain is already built; we'll have to do everything by hand.
180+
p.Consumers = append(p.Consumers, consumer)
181+
consumer.Provider = p.CosmosChain
182+
relayerWallet, err := consumer.BuildRelayerWallet(ctx, "relayer-"+consumer.Config().ChainID)
183+
if err != nil {
184+
return nil, err
185+
}
186+
wallets := make([]ibc.Wallet, len(p.Validators)+1)
187+
wallets[0] = relayerWallet
188+
// This is a hack, but we need to create wallets for the validators that have the right moniker.
189+
for i := 1; i <= len(p.Validators); i++ {
190+
wallets[i], err = consumer.BuildRelayerWallet(ctx, ValidatorMoniker)
191+
if err != nil {
192+
return nil, err
193+
}
194+
}
195+
walletAmounts := make([]ibc.WalletAmount, len(wallets))
196+
for i, wallet := range wallets {
197+
walletAmounts[i] = ibc.WalletAmount{
198+
Address: wallet.FormattedAddress(),
199+
Denom: consumer.Config().Denom,
200+
Amount: sdkmath.NewInt(TotalValidatorFunds),
201+
}
202+
}
203+
204+
ic := interchaintest.NewInterchain().
205+
AddChain(consumer, walletAmounts...).
206+
AddRelayer(relayer, "relayer")
207+
208+
if err := ic.Build(ctx, GetRelayerExecReporter(ctx), interchaintest.InterchainBuildOptions{
209+
Client: dockerClient,
210+
NetworkID: dockerNetwork,
211+
TestName: p.GetNode().TestName,
212+
}); err != nil {
213+
return nil, err
214+
}
215+
216+
for i, val := range consumer.Validators {
217+
if err := val.RecoverKey(ctx, ValidatorMoniker, wallets[i+1].Mnemonic()); err != nil {
218+
return nil, err
219+
}
220+
}
221+
consumerChain, err := chainFromCosmosChain(consumer, relayerWallet, p.TestWallets)
222+
if err != nil {
223+
return nil, err
224+
}
225+
226+
return consumerChain, nil
227+
}
228+
229+
// GetUnusedTestingAddresss retrieves an unused wallet address and its key name safely
230+
func (p *Chain) GetUnusedTestingAddresss() (formattedAddress string, keyName string, err error) {
231+
p.walletMtx.Lock()
232+
defer p.walletMtx.Unlock()
233+
234+
for i, wallet := range p.TestWallets {
235+
if !p.walletsInUse[i] {
236+
p.walletsInUse[i] = true
237+
return wallet.FormattedAddress(), wallet.KeyName(), nil
238+
}
239+
}
240+
241+
return "", "", fmt.Errorf("no unused wallets available")
242+
}
243+
162244
// UpdateAndVerifyStakeChange updates the staking amount on the provider chain and verifies that the change is reflected on the consumer side
163245
func (p *Chain) UpdateAndVerifyStakeChange(ctx context.Context, consumer *Chain, relayer *Relayer, amount, valIdx int) error {
164246

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

637+
func (c *Chain) GetProviderInfo(ctx context.Context) (ProviderInfoResponse, error) {
638+
queryRes, _, err := c.GetNode().ExecQuery(
639+
ctx,
640+
"ccvconsumer", "provider-info",
641+
)
642+
if err != nil {
643+
return ProviderInfoResponse{}, err
644+
}
645+
646+
var queryResponse ProviderInfoResponse
647+
err = json.Unmarshal([]byte(queryRes), &queryResponse)
648+
if err != nil {
649+
return ProviderInfoResponse{}, err
650+
}
651+
652+
return queryResponse, nil
653+
}
654+
655+
func (c *Chain) QueryJSON(ctx context.Context, jsonPath string, query ...string) (gjson.Result, error) {
656+
stdout, _, err := c.GetNode().ExecQuery(ctx, query...)
657+
if err != nil {
658+
return gjson.Result{}, err
659+
}
660+
retval := gjson.GetBytes(stdout, jsonPath)
661+
if !retval.Exists() {
662+
return gjson.Result{}, fmt.Errorf("json path %s not found in query result %s", jsonPath, stdout)
663+
}
664+
return retval, nil
665+
}
666+
555667
func getEvtAttribute(events []abci.Event, evtType string, key string) (string, bool) {
556668
for _, evt := range events {
557669
if evt.GetType() == evtType {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package chainsuite
2+
3+
import (
4+
"context"
5+
"errors"
6+
"strconv"
7+
"time"
8+
9+
providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types"
10+
"github.com/strangelove-ventures/interchaintest/v8"
11+
"github.com/strangelove-ventures/interchaintest/v8/chain/cosmos"
12+
"github.com/strangelove-ventures/interchaintest/v8/ibc"
13+
"github.com/strangelove-ventures/interchaintest/v8/testutil"
14+
)
15+
16+
func GetConsumerSpec(ctx context.Context, providerChain *Chain, proposalMsg *providertypes.MsgCreateConsumer) *interchaintest.ChainSpec {
17+
fullNodes := FullNodeCount
18+
validators := 1
19+
20+
return &interchaintest.ChainSpec{
21+
ChainName: ConsumerChainID,
22+
NumFullNodes: &fullNodes,
23+
NumValidators: &validators,
24+
ChainConfig: ibc.ChainConfig{
25+
ChainID: ConsumerChainID,
26+
Bin: ConsumerBin,
27+
Denom: Stake,
28+
Type: CosmosChainType,
29+
GasPrices: GasPrices + Stake,
30+
GasAdjustment: 2.0,
31+
TrustingPeriod: "336h",
32+
CoinType: "118",
33+
Images: []ibc.DockerImage{
34+
{
35+
Repository: ConsumerImageName(),
36+
Version: ConsumerImageVersion(),
37+
UIDGID: "1025:1025",
38+
},
39+
},
40+
ConfigFileOverrides: map[string]any{
41+
"config/config.toml": DefaultConfigToml(),
42+
},
43+
PreGenesis: func(consumer ibc.Chain) error {
44+
// note that if Top_N>0 proposal will be rejected. If there is a need to support this in the future,
45+
// it is necessary to first submit a create message with Top_N=0 and then an update message with Top_N>0
46+
consumerID, err := providerChain.CreateConsumer(ctx, proposalMsg, ValidatorMoniker)
47+
if err != nil {
48+
return err
49+
}
50+
51+
for index := 0; index < len(providerChain.Validators); index++ {
52+
if err := providerChain.OptIn(ctx, consumerID, index); err != nil {
53+
return err
54+
}
55+
}
56+
57+
// speed up chain launch by submitting update msg with current time as a spawn time
58+
proposalMsg.InitializationParameters.SpawnTime = time.Now()
59+
updateMsg := &providertypes.MsgUpdateConsumer{
60+
ConsumerId: consumerID,
61+
Owner: providerChain.ValidatorWallets[0].Address,
62+
NewOwnerAddress: providerChain.ValidatorWallets[0].Address,
63+
InitializationParameters: proposalMsg.InitializationParameters,
64+
PowerShapingParameters: proposalMsg.PowerShapingParameters,
65+
}
66+
if err := providerChain.UpdateConsumer(ctx, updateMsg, ValidatorMoniker); err != nil {
67+
return err
68+
}
69+
70+
if err := testutil.WaitForBlocks(ctx, 2, providerChain); err != nil {
71+
return err
72+
}
73+
74+
consumerChain, err := providerChain.GetConsumerChainByChainId(ctx, proposalMsg.ChainId)
75+
if err != nil {
76+
return err
77+
}
78+
79+
if consumerChain.Phase != providertypes.CONSUMER_PHASE_LAUNCHED.String() {
80+
return errors.New("consumer chain is not launched")
81+
}
82+
83+
return nil
84+
},
85+
Bech32Prefix: Bech32PrefixConsumer,
86+
ModifyGenesisAmounts: DefaultGenesisAmounts(Stake),
87+
ModifyGenesis: cosmos.ModifyGenesis(consumerModifiedGenesis()),
88+
InterchainSecurityConfig: ibc.ICSConfig{
89+
ConsumerCopyProviderKey: func(int) bool { return true },
90+
},
91+
},
92+
}
93+
}
94+
95+
func consumerModifiedGenesis() []cosmos.GenesisKV {
96+
return []cosmos.GenesisKV{
97+
cosmos.NewGenesisKV("app_state.slashing.params.signed_blocks_window", strconv.Itoa(SlashingWindowConsumer)),
98+
cosmos.NewGenesisKV("consensus.params.block.max_gas", "50000000"),
99+
cosmos.NewGenesisKV("app_state.ccvconsumer.params.soft_opt_out_threshold", "0.0"),
100+
cosmos.NewGenesisKV("app_state.ccvconsumer.params.blocks_per_distribution_transmission", BlocksPerDistribution),
101+
cosmos.NewGenesisKV("app_state.ccvconsumer.params.reward_denoms", []string{Stake}),
102+
}
103+
}

0 commit comments

Comments
 (0)