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

feat: ics v6 support #1306

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
116 changes: 99 additions & 17 deletions chain/cosmos/chain_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"math/rand"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -42,7 +41,7 @@ import (
"google.golang.org/grpc/credentials/insecure"

icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types"
ccvclient "github.com/cosmos/interchain-security/v5/x/ccv/provider/client"
providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types"
"github.com/strangelove-ventures/interchaintest/v8/blockdb"
"github.com/strangelove-ventures/interchaintest/v8/dockerutil"
"github.com/strangelove-ventures/interchaintest/v8/ibc"
Expand Down Expand Up @@ -882,25 +881,108 @@ func (tn *ChainNode) SendIBCTransfer(
return tn.ExecTx(ctx, keyName, command...)
}

func (tn *ChainNode) ConsumerAdditionProposal(ctx context.Context, keyName string, prop ccvclient.ConsumerAdditionProposalJSON) (string, error) {
propBz, err := json.Marshal(prop)
if err != nil {
return "", err
}
type CreateConsumerResponse struct {
// ConsumerID can be empty if submited via a gov proposal.
ConsumerID string `json:"consumer_id"`
TxHash string `json:"tx_hash"`
}

fileName := "proposal_" + dockerutil.RandLowerCaseLetterString(4) + ".json"
func (tn *ChainNode) ConsumerAdditionProposal(ctx context.Context, keyName string, cc providertypes.MsgCreateConsumer, deposit string) (CreateConsumerResponse, error) {
cosmosChain := (tn.Chain).(*CosmosChain)

fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName)
if err := fw.WriteFile(ctx, tn.VolumeName, fileName, propBz); err != nil {
return "", fmt.Errorf("failure writing proposal json: %w", err)
}
// authority, err := cosmosChain.GetGovernanceAddress(ctx)
// if err != nil {
// return "", err
// }

filePath := filepath.Join(tn.HomeDir(), fileName)
// permissionless
if tn.HasCommand(ctx, "tx", "provider", "create-consumer") {
fmt.Println("Using new command")
content, err := json.Marshal(cc)
if err != nil {
return CreateConsumerResponse{}, err
}
jsonFile := "create-consumer.json"
if err = tn.WriteFile(ctx, content, jsonFile); err != nil {
return CreateConsumerResponse{}, err
}
filePath := path.Join(tn.HomeDir(), jsonFile)
txHash, err := tn.ExecTx(ctx, keyName, "provider", "create-consumer", filePath)
if err != nil {
return CreateConsumerResponse{}, err
}

return tn.ExecTx(ctx, keyName,
"gov", "submit-legacy-proposal", "consumer-addition", filePath,
"--gas", "auto",
)
response, err := tn.TxHashToResponse(ctx, txHash)
if err != nil {
return CreateConsumerResponse{}, err
}

consumerId, found := getEvtAttribute(response.Events, providertypes.EventTypeCreateConsumer, providertypes.AttributeConsumerId)
if !found {
return CreateConsumerResponse{}, fmt.Errorf("consumer id is not found")
}
fmt.Println("Consumer ID: ", consumerId)

return CreateConsumerResponse{
ConsumerID: consumerId,
TxHash: txHash,
}, nil

// TODO: remove support for this? or as default for v1 non permissionless chains
} else if !tn.HasCommand(ctx, "tx", "gov", "submit-legacy-proposal", "consumer-addition") {
ccvProp := &providertypes.MsgCreateConsumer{
ChainId: cc.ChainId,
Submitter: cc.Submitter,
Metadata: providertypes.ConsumerMetadata{
Name: cc.ChainId,
Description: "description",
Metadata: "github.com/meta/data",
},
InitializationParameters: nil,
PowerShapingParameters: nil,
AllowlistedRewardDenoms: nil,
}
text := "consumer addition " + cc.ChainId
propObj, err := cosmosChain.BuildProposal([]ProtoMessage{ccvProp}, text, text, "ipfs://CID", deposit, "", false)
if err != nil {
return CreateConsumerResponse{}, err
}
resp, err := tn.SubmitProposal(ctx, keyName, propObj)
if err != nil {
return CreateConsumerResponse{}, err
}

// Gov
return CreateConsumerResponse{
ConsumerID: "",
TxHash: resp,
}, nil

} else {
// propBz, err := json.Marshal(cc)
// if err != nil {
// return "", err
// }
// propBz, err = sjson.SetBytes(propBz, "metadata", "ipfs://CID")
// if err != nil {
// return "", err
// }

// fileName := "proposal_" + dockerutil.RandLowerCaseLetterString(4) + ".json"

// fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName)
// if err := fw.WriteFile(ctx, tn.VolumeName, fileName, propBz); err != nil {
// return "", fmt.Errorf("failure writing proposal json: %w", err)
// }

// filePath := filepath.Join(tn.HomeDir(), fileName)

// return tn.ExecTx(ctx, keyName,
// "gov", "submit-legacy-proposal", "consumer-addition", filePath,
// "--gas", "auto",
// )
panic("ICT has not implemented non govv1 or permissionless. You can no longer use submit-legacy-proposal for ICS additions.")
}
}

func (tn *ChainNode) GetTransaction(clientCtx client.Context, txHash string) (*sdk.TxResponse, error) {
Expand Down
2 changes: 1 addition & 1 deletion chain/cosmos/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
transfer "github.com/cosmos/ibc-go/v8/modules/apps/transfer"
ibccore "github.com/cosmos/ibc-go/v8/modules/core"
ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint"
ccvprovider "github.com/cosmos/interchain-security/v5/x/ccv/provider"
ccvprovider "github.com/cosmos/interchain-security/v6/x/ccv/provider"
ibcwasm "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos/08-wasm-types"
)

Expand Down
14 changes: 9 additions & 5 deletions chain/cosmos/cosmos_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" // nolint:staticcheck
chanTypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
ccvclient "github.com/cosmos/interchain-security/v5/x/ccv/provider/client"
providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types"
dockertypes "github.com/docker/docker/api/types"
volumetypes "github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
Expand Down Expand Up @@ -509,13 +509,13 @@ func (c *CosmosChain) QueryBankMetadata(ctx context.Context, denom string) (*Ban
return c.getFullNode().QueryBankMetadata(ctx, denom)
}

// ConsumerAdditionProposal submits a legacy governance proposal to add a consumer to the chain.
func (c *CosmosChain) ConsumerAdditionProposal(ctx context.Context, keyName string, prop ccvclient.ConsumerAdditionProposalJSON) (tx TxProposal, _ error) {
txHash, err := c.getFullNode().ConsumerAdditionProposal(ctx, keyName, prop)
// CreateConsumerAction submits a consumer to the chain.
func (c *CosmosChain) CreateConsumerAction(ctx context.Context, keyName string, cc providertypes.MsgCreateConsumer, deposit string) (tx TxProposal, _ error) {
ca, err := c.getFullNode().ConsumerAdditionProposal(ctx, keyName, cc, deposit)
if err != nil {
return tx, fmt.Errorf("failed to submit consumer addition proposal: %w", err)
}
return c.txProposal(txHash)
return c.txProposal(ca.TxHash)
}

func (c *CosmosChain) txProposal(txHash string) (tx TxProposal, _ error) {
Expand Down Expand Up @@ -600,6 +600,10 @@ func (c *CosmosChain) GetGasFeesInNativeDenom(gasPaid int64) int64 {
return int64(math.Ceil(fees))
}

func (c *CosmosChain) ChangeBinary(ctx context.Context, binary string) {
c.cfg.Bin = binary
}

func (c *CosmosChain) UpgradeVersion(ctx context.Context, cli *client.Client, containerRepo, version string) {
c.cfg.Images[0].Version = version
for _, n := range c.Validators {
Expand Down
104 changes: 80 additions & 24 deletions chain/cosmos/ics.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import (
"time"

sdkmath "cosmossdk.io/math"
abci "github.com/cometbft/cometbft/abci/types" // nolint:staticcheck
"github.com/cosmos/cosmos-sdk/types"
govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" // nolint:staticcheck
ccvclient "github.com/cosmos/interchain-security/v5/x/ccv/provider/client"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types"
"github.com/icza/dyno"
"github.com/strangelove-ventures/interchaintest/v8/dockerutil"
"github.com/strangelove-ventures/interchaintest/v8/ibc"
Expand Down Expand Up @@ -148,7 +149,6 @@ func (c *CosmosChain) StartProvider(testName string, ctx context.Context, additi
if err != nil {
return fmt.Errorf("failed to parse trusting period in 'StartProvider': %w", err)
}

spawnTimeWait := os.Getenv("ICS_SPAWN_TIME_WAIT")
if spawnTimeWait == "" {
spawnTimeWait = "45s"
Expand All @@ -157,32 +157,55 @@ func (c *CosmosChain) StartProvider(testName string, ctx context.Context, additi
if err != nil {
return fmt.Errorf("invalid ICS_SPAWN_TIME_WAIT %s: %w", spawnTimeWait, err)
}

for _, consumer := range c.Consumers {
prop := ccvclient.ConsumerAdditionProposalJSON{
Title: fmt.Sprintf("Addition of %s consumer chain", consumer.cfg.Name),
Summary: "Proposal to add new consumer chain",
ChainId: consumer.cfg.ChainID,
InitialHeight: clienttypes.Height{RevisionNumber: clienttypes.ParseChainID(consumer.cfg.ChainID), RevisionHeight: 1},
GenesisHash: []byte("gen_hash"),
BinaryHash: []byte("bin_hash"),
SpawnTime: time.Now().Add(spawnTimeWaitDuration),

// TODO fetch or default variables
BlocksPerDistributionTransmission: 1000,
CcvTimeoutPeriod: trustingPeriod * 2,
TransferTimeoutPeriod: trustingPeriod,
ConsumerRedistributionFraction: "0.75",
HistoricalEntries: 10000,
UnbondingPeriod: trustingPeriod,
Deposit: "100000000" + c.cfg.Denom,
prop := providertypes.MsgCreateConsumer{
// Title: fmt.Sprintf("Addition of %s consumer chain", consumer.cfg.Name),
// Summary: "Proposal to add new consumer chain",
// ChainId: consumer.cfg.ChainID,
// InitialHeight: clienttypes.Height{RevisionNumber: clienttypes.ParseChainID(consumer.cfg.ChainID), RevisionHeight: 1},
// GenesisHash: []byte("gen_hash"),
// BinaryHash: []byte("bin_hash"),
// SpawnTime: time.Now().Add(spawnTimeWaitDuration),

// // TODO fetch or default variables
// BlocksPerDistributionTransmission: 1000,
// CcvTimeoutPeriod: trustingPeriod * 2,
// TransferTimeoutPeriod: trustingPeriod,
// ConsumerRedistributionFraction: "0.75",
// HistoricalEntries: 10000,
// UnbondingPeriod: trustingPeriod,
// Deposit: "100000000" + c.cfg.Denom,
Submitter: proposerAddr,
ChainId: consumer.cfg.ChainID,
Metadata: providertypes.ConsumerMetadata{
Name: consumer.cfg.Name,
Description: fmt.Sprintf("Consumer chain %s", consumer.cfg.Name),
Metadata: "metadata",
},
InitializationParameters: &providertypes.ConsumerInitializationParameters{
InitialHeight: clienttypes.Height{RevisionNumber: clienttypes.ParseChainID(consumer.cfg.ChainID), RevisionHeight: 1},
GenesisHash: []byte("gen_hash"),
BinaryHash: []byte("bin_hash"),
SpawnTime: time.Now().Add(spawnTimeWaitDuration),
UnbondingPeriod: trustingPeriod,
CcvTimeoutPeriod: trustingPeriod * 2,
TransferTimeoutPeriod: trustingPeriod,
ConsumerRedistributionFraction: "0.75",
BlocksPerDistributionTransmission: 1000,
HistoricalEntries: 10000,
DistributionTransmissionChannel: "distribution",
},
PowerShapingParameters: nil,
AllowlistedRewardDenoms: nil,
}

height, err := c.Height(ctx)
if err != nil {
return fmt.Errorf("failed to query provider height before consumer addition proposal: %w", err)
}

propTx, err := c.ConsumerAdditionProposal(ctx, proposerKeyName, prop)
propTx, err := c.CreateConsumerAction(ctx, proposerKeyName, prop, "")
if err != nil {
return err
}
Expand Down Expand Up @@ -245,6 +268,16 @@ func (c *CosmosChain) StartConsumer(testName string, ctx context.Context, additi
return err
}

consumerID := c.cfg.ChainID
consumerChains, _, err := c.Provider.GetNode().ExecQuery(ctx, "provider", "list-consumer-chains")
if err != nil {
return err
}
consumerChain := gjson.GetBytes(consumerChains, fmt.Sprintf("chains.#(chain_id=%q)", c.cfg.ChainID))
if consumerChain.Get("consumer_id").Exists() {
consumerID = consumerChain.Get("consumer_id").String()
}

// Copy provider priv val keys to these nodes
for i, val := range c.Provider.Validators {
i := i
Expand All @@ -265,7 +298,7 @@ func (c *CosmosChain) StartConsumer(testName string, ctx context.Context, additi
return fmt.Errorf("failed to get consumer validator pubkey: %w", err)
}
keyStr := strings.TrimSpace(string(key))
_, err = c.Provider.Validators[i].ExecTx(ctx, valKey, "provider", "assign-consensus-key", c.cfg.ChainID, keyStr)
_, err = c.Provider.Validators[i].ExecTx(ctx, valKey, "provider", "assign-consensus-key", consumerID, keyStr)
if err != nil {
return fmt.Errorf("failed to assign consumer validator pubkey: %w", err)
}
Expand All @@ -289,7 +322,11 @@ func (c *CosmosChain) StartConsumer(testName string, ctx context.Context, additi
if err != nil {
return fmt.Errorf("failed to query proposed chains: %w", err)
}
spawnTime := gjson.GetBytes(proposals, fmt.Sprintf("proposals.#(messages.0.content.chain_id==%q).messages.0.content.spawn_time", c.cfg.ChainID)).Time()
msgContent := gjson.GetBytes(proposals, fmt.Sprintf("proposals.#(messages.0.content.chain_id==%q).messages.0.content", c.cfg.ChainID))
if !msgContent.Exists() {
msgContent = gjson.GetBytes(proposals, fmt.Sprintf("proposals.#(messages.0.value.chain_id==%q).messages.0.value", c.cfg.ChainID))
}
spawnTime := msgContent.Get("spawn_time").Time()
c.log.Info("Waiting for chain to spawn", zap.Time("spawn_time", spawnTime), zap.String("chain_id", c.cfg.ChainID))
time.Sleep(time.Until(spawnTime))
if err := testutil.WaitForBlocks(ctx, 2, c.Provider); err != nil {
Expand All @@ -309,7 +346,11 @@ func (c *CosmosChain) StartConsumer(testName string, ctx context.Context, additi
return err
}

ccvStateMarshaled, _, err := c.Provider.GetNode().ExecQuery(ctx, "provider", "consumer-genesis", c.cfg.ChainID)
consumerId, err := c.Provider.GetNode().GetConsumerChainByChainId(ctx, c.cfg.ChainID)
if err != nil {
return err
}
ccvStateMarshaled, _, err := c.Provider.GetNode().ExecQuery(ctx, "provider", "consumer-genesis", consumerId)
if err != nil {
return fmt.Errorf("failed to query provider for ccv state: %w", err)
}
Expand Down Expand Up @@ -461,3 +502,18 @@ func (c *CosmosChain) transformCCVState(ctx context.Context, ccvState []byte, co
}
return res.Stdout, nil
}

// https://github.com/cosmos/interchain-security/blob/deea6fb8a98bbce447a1b2dfd6a60806141efbfe/tests/interchain/chainsuite/chain.go#L442
func getEvtAttribute(events []abci.Event, evtType string, key string) (string, bool) {
for _, evt := range events {
if evt.GetType() == evtType {
for _, attr := range evt.Attributes {
if attr.Key == key {
return attr.Value, true
}
}
}
}

return "", false
}
Loading