Skip to content

Commit

Permalink
further, panic on the ICS consumer though
Browse files Browse the repository at this point in the history
  • Loading branch information
Reecepbcups committed Nov 26, 2024
1 parent 486737b commit 37646b5
Show file tree
Hide file tree
Showing 2 changed files with 282 additions and 23 deletions.
303 changes: 280 additions & 23 deletions interchaintest/ics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,63 @@ package e2e

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

sdkmath "cosmossdk.io/math"
abcitypes "github.com/cometbft/cometbft/abci/types"
govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/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/relayer"
"github.com/strangelove-ventures/interchaintest/v8/testreporter"
"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
"go.uber.org/zap/zaptest"
)

type ConsumerConfig struct {
ChainName string
Version string
Denom string
TopN int
ValidatorSetCap int
ValidatorPowerCap int
AllowInactiveVals bool
MinStake uint64
Allowlist []string
Denylist []string
spec *interchaintest.ChainSpec
}

var (
icsPath = "ics-path"
// cd testing/consumer && DOCKER_BUILDKIT=0 docker build . --tag consumer:local
ConsumerTestingChain = interchaintest.ChainSpec{
ConsumerTestingChainSpec = interchaintest.ChainSpec{
Name: "ics-consumer",
Version: "v5.0.0",
Version: "v6.0.0",
NumValidators: &vals, NumFullNodes: &fNodes,
ChainConfig: ibc.ChainConfig{
GasAdjustment: 3.0,
TrustingPeriod: "504h",
Type: "cosmos",
// Name: "consumer",
ChainID: "localchain-2",
// Bin: "consumerd",
// Denom: "utoken",
// CoinType: "118",
// Bech32Prefix: "cosmos",
ChainID: "ics-consumer-1",
Bin: "interchain-security-cd",
Denom: "stake",
Bech32Prefix: "consumer",
// GasPrices: "0.0" + "utoken",
// InterchainSecurityConfig: ibc.ICSConfig{
// ProviderVerOverride: "v0.0.0",
// ConsumerVerOverride: "v0.0.0",
// },
InterchainSecurityConfig: ibc.ICSConfig{
ProviderVerOverride: "v4.1.0", // semver.Compare(p.GetNode().ICSVersion(ctx), "v4.1.0") > 0 && config.spec.InterchainSecurityConfig.ProviderVerOverride == ""
// ConsumerVerOverride: "v0.0.0",
},
ModifyGenesis: cosmos.ModifyGenesis([]cosmos.GenesisKV{
cosmos.NewGenesisKV("app_state.gov.params.voting_period", VotingPeriod),
cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", MaxDepositPeriod),
Expand Down Expand Up @@ -72,18 +96,18 @@ func TestICSConnection(t *testing.T) {

// Setup Interchain
ic := interchaintest.NewInterchain().
AddChain(provider).
// AddChain(consumer).
AddRelayer(r, "rly")
// AddProviderConsumerLink(interchaintest.ProviderConsumerLink{
// Provider: provider,
// Consumer: consumer,
// Relayer: r,
// Path: icsPath,
// })

// failed to start chains: failed to start provider chain prysm: failed to submit consumer addition proposal: exit
// use Violets latest patch>?
AddChain(provider)
// AddChain(consumer).
// AddRelayer(r, "rly") // will do later
// AddProviderConsumerLink(interchaintest.ProviderConsumerLink{
// Provider: provider,
// Consumer: consumer,
// Relayer: r,
// Path: icsPath,
// })

// failed to start chains: failed to start provider chain prysm: failed to submit consumer addition proposal: exit
// use Violets latest patch>?
require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{
TestName: t.Name(),
Client: client,
Expand All @@ -94,6 +118,68 @@ func TestICSConnection(t *testing.T) {
_ = ic.Close()
})

// Add consumer chain after the startup
spawnTime := time.Now().Add(time.Second * 30) // too long?
chainID := fmt.Sprintf("%s-%d", ConsumerTestingChainSpec.ChainName, 2)

consumerCfg := ConsumerConfig{
ChainName: ConsumerTestingChainSpec.Name,
Version: ConsumerTestingChainSpec.Version,
Denom: ConsumerTestingChainSpec.Denom,
TopN: 100,
AllowInactiveVals: true,
MinStake: 1_000_000,
}

err = CreateConsumerPermissionless(ctx, provider, chainID, consumerCfg, spawnTime)
require.NoError(t, err)

cf = interchaintest.NewBuiltinChainFactory(
zaptest.NewLogger(t),
[]*interchaintest.ChainSpec{&ConsumerTestingChainSpec},
)
chains2, err := cf.Chains(provider.GetNode().TestName)
require.NoError(t, err)

// We can't use AddProviderConsumerLink here because the provider chain is already built; we'll have to do everything by hand.
cosmosConsumer := chains2[0].(*cosmos.CosmosChain)
// consumers := []*cosmos.CosmosChain{cosmosConsumer}

relayerWallet, err := cosmosConsumer.BuildRelayerWallet(ctx, "relayer-"+cosmosConsumer.Config().ChainID)
require.NoError(t, err)

wallets := make([]ibc.Wallet, len(provider.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(provider.Validators); i++ {
wallets[i], err = cosmosConsumer.BuildRelayerWallet(ctx, "validator") // hardcoded in ICT
require.NoError(t, err)
}
walletAmounts := make([]ibc.WalletAmount, len(wallets))
for i, wallet := range wallets {
walletAmounts[i] = ibc.WalletAmount{
Address: wallet.FormattedAddress(),
Denom: cosmosConsumer.Config().Denom,
Amount: sdkmath.NewInt(11_000_000_000), // gaia release/v21.x ValidatorFunds @7c0c4cbb7b26c48b235f60532223a74f4f68b830
}
}
ic = interchaintest.NewInterchain().
AddChain(cosmosConsumer, walletAmounts...).
AddRelayer(r, "relayer")

err = ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{
Client: client,
NetworkID: network,
TestName: provider.GetNode().TestName,
})
require.NoError(t, err)

// setup chain keys, stop & start relayer, then connect provider <> consumer

// manual stuff

/// ---

// require.NoError(t, provider.FinishICSProviderSetup(ctx, r, eRep, icsPath))

// amt := math.NewInt(10_000_000)
Expand Down Expand Up @@ -151,3 +237,174 @@ func TestICSConnection(t *testing.T) {
// })

}

func CreateConsumerPermissionless(ctx context.Context, p *cosmos.CosmosChain, 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,
Allowlist: config.Allowlist,
Denylist: config.Denylist,
}
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 := QueryJSON(ctx, p, 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", "", "5000000"+p.Config().Denom, "", false)
if err != nil {
return err
}
txhash, err := p.GetNode().SubmitProposal(ctx, "validator", prop)
if err != nil {
return err
}
propID, err := GetProposalID(ctx, p, txhash)
if err != nil {
return err
}
if err := PassProposal(ctx, p, propID); err != nil {
return err
}
}
return nil
}

func QueryJSON(ctx context.Context, c *cosmos.CosmosChain, 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
}

// GetProposalID parses the proposal ID from the tx; necessary when the proposal type isn't accessible to interchaintest yet
func GetProposalID(ctx context.Context, c *cosmos.CosmosChain, txhash string) (string, error) {
stdout, _, err := c.GetNode().ExecQuery(ctx, "tx", txhash)
if err != nil {
return "", err
}
result := struct {
Events []abcitypes.Event `json:"events"`
}{}
if err := json.Unmarshal(stdout, &result); err != nil {
return "", err
}
for _, event := range result.Events {
if event.Type == "submit_proposal" {
for _, attr := range event.Attributes {
if string(attr.Key) == "proposal_id" {
return string(attr.Value), nil
}
}
}
}
return "", fmt.Errorf("proposal ID not found in tx %s", txhash)
}

func PassProposal(ctx context.Context, c *cosmos.CosmosChain, proposalID string) error {
propID, err := strconv.ParseInt(proposalID, 10, 64)
if err != nil {
return err
}
err = c.VoteOnProposalAllValidators(ctx, uint64(propID), cosmos.ProposalVoteYes)
if err != nil {
return err
}
return WaitForProposalStatus(ctx, c, proposalID, govv1.StatusPassed)
}

func WaitForProposalStatus(ctx context.Context, c *cosmos.CosmosChain, proposalID string, status govv1.ProposalStatus) error {
propID, err := strconv.ParseInt(proposalID, 10, 64)
if err != nil {
return err
}
chainHeight, err := c.Height(ctx)
if err != nil {
return err
}
// At 4s per block, 75 blocks is about 5 minutes.
maxHeight := chainHeight + 75
_, err = cosmos.PollForProposalStatusV1(ctx, c, chainHeight, maxHeight, uint64(propID), status)
return err
}
2 changes: 2 additions & 0 deletions interchaintest/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ var (
cosmos.NewGenesisKV("app_state.tokenfactory.params.denom_creation_gas_consume", 1), // cost 1 gas to create a new denom
// v4+ ICS provider required
cosmos.NewGenesisKV("app_state.provider.params.blocks_per_epoch", "1"),
cosmos.NewGenesisKV("app_state.provider.params.slash_meter_replenish_period", "2s"),
cosmos.NewGenesisKV("app_state.provider.params.slash_meter_replenish_fraction", "1.00"),
}

DefaultChainConfig = ibc.ChainConfig{
Expand Down

0 comments on commit 37646b5

Please sign in to comment.