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

refactor(v7): get consumer genesis from provider chain and transform it #1093

Merged
merged 1 commit into from
Apr 25, 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
19 changes: 19 additions & 0 deletions chain/cosmos/chain_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
dockerclient "github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"go.uber.org/zap"
"golang.org/x/mod/semver"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
Expand Down Expand Up @@ -721,6 +722,24 @@ func (tn *ChainNode) IsAboveSDK47(ctx context.Context) bool {
return tn.HasCommand(ctx, "genesis")
}

// ICSVersion returns the version of interchain-security the binary was built with.
// If it doesn't depend on interchain-security, it returns an empty string.
func (tn *ChainNode) ICSVersion(ctx context.Context) string {
if strings.HasPrefix(tn.Chain.Config().Bin, "interchain-security") {
// This isn't super pretty, but it's the best we can do for an interchain-security binary.
// It doesn't depend on itself, and the version command doesn't actually output a version.
// Ideally if you have a binary called something like "v3.3.0-my-fix" you can use it as a version, since the v3.3.0 part is in it.
return semver.Canonical(tn.Image.Version)
Reecepbcups marked this conversation as resolved.
Show resolved Hide resolved
}
info := tn.GetBuildInformation(ctx)
for _, dep := range info.BuildDeps {
if strings.HasPrefix(dep.Parent, "github.com/cosmos/interchain-security") {
return semver.Canonical(dep.Version)
}
}
return ""
}

// AddGenesisAccount adds a genesis account for each key
func (tn *ChainNode) AddGenesisAccount(ctx context.Context, address string, genesisAmount []types.Coin) error {
amount := ""
Expand Down
123 changes: 49 additions & 74 deletions chain/cosmos/cosmos_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ import (
"io"
"math"
"os"
"path"
"strconv"
"strings"
"sync"
"time"

govv1beta1 "cosmossdk.io/api/cosmos/gov/v1beta1"
sdkmath "cosmossdk.io/math"
abcitypes "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/proto/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
Expand All @@ -34,11 +33,7 @@ import (
cosmosproto "github.com/cosmos/gogoproto/proto"
clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
chanTypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types"
ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint"
ccvconsumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types"
ccvclient "github.com/cosmos/interchain-security/v3/x/ccv/provider/client"
ccvprovidertypes "github.com/cosmos/interchain-security/v3/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 All @@ -50,6 +45,7 @@ import (
"github.com/strangelove-ventures/interchaintest/v7/internal/dockerutil"
"github.com/strangelove-ventures/interchaintest/v7/testutil"
"go.uber.org/zap"
"golang.org/x/mod/semver"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
Expand Down Expand Up @@ -1197,6 +1193,47 @@ func (c *CosmosChain) StartProvider(testName string, ctx context.Context, additi
return nil
}

func (c *CosmosChain) transformCCVState(ctx context.Context, ccvState []byte, consumerVersion, providerVersion string) ([]byte, error) {
Reecepbcups marked this conversation as resolved.
Show resolved Hide resolved
// If they're both under 3.3.0, or if they're the same version, we don't need to transform the state.
if semver.MajorMinor(providerVersion) == semver.MajorMinor(consumerVersion) ||
(semver.Compare(providerVersion, "v3.3.0") < 0 && semver.Compare(consumerVersion, "v3.3.0") < 0) {
return ccvState, nil
}
var imageVersion, toVersion string
// The trick here is that when we convert the state to a consumer < 3.3.0, we need a converter that knows about that version; those are >= 4.0.0, and need a --to flag.
// Other than that, this is a question of using whichever version is newer. If it's the provider's, we need a --to flag to tell it the consumer version.
// If it's the consumer's, we don't need a --to flag cause it'll assume the consumer version.
if semver.Compare(providerVersion, "v3.3.0") >= 0 && semver.Compare(providerVersion, consumerVersion) > 0 {
imageVersion = "v4.0.0"
if semver.Compare(providerVersion, "v4.0.0") > 0 {
imageVersion = providerVersion
}
toVersion = semver.Major(consumerVersion)
if toVersion == "v3" {
toVersion = semver.MajorMinor(consumerVersion)
}
} else {
imageVersion = consumerVersion
}
err := c.GetNode().WriteFile(ctx, ccvState, "ccvconsumer.json")
if err != nil {
return nil, fmt.Errorf("failed to write ccv state to file: %w", err)
}
job := dockerutil.NewImage(c.log, c.GetNode().DockerClient, c.GetNode().NetworkID,
c.GetNode().TestName, "ghcr.io/strangelove-ventures/heighliner/ics", imageVersion,
)
cmd := []string{"interchain-security-cd", "genesis", "transform"}
if toVersion != "" {
cmd = append(cmd, "--to", toVersion+".x")
}
cmd = append(cmd, path.Join(c.GetNode().HomeDir(), "ccvconsumer.json"))
res := job.Run(ctx, cmd, dockerutil.ContainerOptions{Binds: c.GetNode().Bind()})
if res.Err != nil {
return nil, fmt.Errorf("failed to transform ccv state: %w", res.Err)
}
return res.Stdout, nil
}

// Bootstraps the consumer chain and starts it from genesis
func (c *CosmosChain) StartConsumer(testName string, ctx context.Context, additionalGenesisWallets ...ibc.WalletAmount) error {
chainCfg := c.Config()
Expand Down Expand Up @@ -1263,83 +1300,21 @@ func (c *CosmosChain) StartConsumer(testName string, ctx context.Context, additi
}
}

providerHeight, err := c.Provider.Height(ctx)
if err != nil {
return fmt.Errorf("failed to query provider height")
}
providerHeightInt64 := int64(providerHeight)

block, err := c.Provider.getFullNode().Client.Block(ctx, &providerHeightInt64)
if err != nil {
return fmt.Errorf("failed to query provider block to initialize consumer client")
}

genbz, err := validator0.GenesisFileContent(ctx)
if err != nil {
return err
}

// populate genesis file ccvconsumer module app_state.
// fetch provider latest block (timestamp, root.hash, and next_validators_hash) to populate provider_consensus_state
// populate provider_client_state with trusting and unbonding periods, latest_height.revision_height of height which is used for consensus state
// populate initial_val_set with provider val pubkeys and power

nextValidatorsHash := block.Block.NextValidatorsHash
timestamp := block.Block.Time
rootHash := block.Block.AppHash

page := int(1)
perPage := int(1000)
providerVals, err := c.Provider.getFullNode().Client.Validators(ctx, &providerHeightInt64, &page, &perPage)
ccvStateMarshaled, _, err := c.Provider.GetNode().ExecQuery(ctx, "provider", "consumer-genesis", c.cfg.ChainID)
if err != nil {
return fmt.Errorf("failed to get provider validators: %w", err)
return fmt.Errorf("failed to query provider for ccv state: %w", err)
}

initialVals := make([]abcitypes.ValidatorUpdate, len(providerVals.Validators))
for i, val := range providerVals.Validators {
initialVals[i] = abcitypes.ValidatorUpdate{
PubKey: crypto.PublicKey{Sum: &crypto.PublicKey_Ed25519{Ed25519: val.PubKey.Bytes()}},
Power: val.VotingPower,
}
}

providerCfg := c.Provider.Config()

clientState := ibctmtypes.NewClientState(
providerCfg.ChainID,
ibctmtypes.DefaultTrustLevel,
DefaultProviderUnbondingPeriod/2,
DefaultProviderUnbondingPeriod, // Needs to match provider unbonding period
ccvprovidertypes.DefaultMaxClockDrift,
clienttypes.Height{
RevisionHeight: uint64(providerHeight),
RevisionNumber: clienttypes.ParseChainID(providerCfg.ChainID),
},
commitmenttypes.GetSDKSpecs(),
defaultUpgradePath,
)

root := commitmenttypes.MerkleRoot{
Hash: rootHash,
}

consensusState := ibctmtypes.NewConsensusState(timestamp, root, nextValidatorsHash)

ccvState := ccvconsumertypes.NewInitialGenesisState(
clientState,
consensusState,
initialVals,
ccvconsumertypes.DefaultGenesisState().GetParams(),
)

ccvState.Params.Enabled = true

ccvStateMarshaled, err := c.cfg.EncodingConfig.Codec.MarshalJSON(ccvState)
c.log.Info("HERE STATE!", zap.String("GEN", string(ccvStateMarshaled)))
consumerICS := c.GetNode().ICSVersion(ctx)
providerICS := c.Provider.GetNode().ICSVersion(ctx)
ccvStateMarshaled, err = c.transformCCVState(ctx, ccvStateMarshaled, consumerICS, providerICS)
Reecepbcups marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return fmt.Errorf("failed to marshal ccv state to json: %w", err)
return err
}

var ccvStateUnmarshaled interface{}
if err := json.Unmarshal(ccvStateMarshaled, &ccvStateUnmarshaled); err != nil {
return fmt.Errorf("failed to unmarshal ccv state json: %w", err)
Expand Down
116 changes: 68 additions & 48 deletions examples/ibc/ics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ibc_test
import (
"context"
"fmt"
"strings"
"testing"
"time"

Expand All @@ -20,7 +21,7 @@ func TestICS(t *testing.T) {
t.Skip("skipping in short mode")
}

tests := []relayerImp{
relayers := []relayerImp{
{
name: "Cosmos Relayer",
relayerImp: ibc.CosmosRly,
Expand All @@ -31,64 +32,83 @@ func TestICS(t *testing.T) {
},
}

for _, tt := range tests {
tt := tt
testname := tt.name
icsVersions := []string{"v3.1.0", "v3.3.0", "v4.0.0"}
Reecepbcups marked this conversation as resolved.
Show resolved Hide resolved

for _, rly := range relayers {
rly := rly
testname := rly.name
t.Run(testname, func(t *testing.T) {
// We paralellize the relayers, but not the versions. That would be too many tests running at once, and things can become unstable.
t.Parallel()
ctx := context.Background()
for _, providerVersion := range icsVersions {
providerVersion := providerVersion
for _, consumerVersion := range icsVersions {
consumerVersion := consumerVersion
testname := fmt.Sprintf("provider%s-consumer%s", providerVersion, consumerVersion)
testname = strings.ReplaceAll(testname, ".", "")
t.Run(testname, func(t *testing.T) {
fullNodes := 0
validators := 1
ctx := context.Background()
var consumerBechPrefix string
if consumerVersion == "v4.0.0" {
consumerBechPrefix = "consumer"
}

// Chain Factory
cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{
{Name: "ics-provider", Version: "v3.1.0", ChainConfig: ibc.ChainConfig{GasAdjustment: 1.5}},
{Name: "ics-consumer", Version: "v3.1.0"},
})
// Chain Factory
cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{
{Name: "ics-provider", Version: providerVersion, NumValidators: &validators, NumFullNodes: &fullNodes, ChainConfig: ibc.ChainConfig{GasAdjustment: 1.5}},
{Name: "ics-consumer", Version: consumerVersion, NumValidators: &validators, NumFullNodes: &fullNodes, ChainConfig: ibc.ChainConfig{Bech32Prefix: consumerBechPrefix}},
})

chains, err := cf.Chains(t.Name())
require.NoError(t, err)
provider, consumer := chains[0], chains[1]
chains, err := cf.Chains(t.Name())
require.NoError(t, err)
provider, consumer := chains[0], chains[1]

// Relayer Factory
client, network := interchaintest.DockerSetup(t)
// Relayer Factory
client, network := interchaintest.DockerSetup(t)

r := interchaintest.NewBuiltinRelayerFactory(
tt.relayerImp,
zaptest.NewLogger(t),
).Build(t, client, network)
r := interchaintest.NewBuiltinRelayerFactory(
rly.relayerImp,
zaptest.NewLogger(t),
).Build(t, client, network)

// Prep Interchain
const ibcPath = "ics-path"
ic := interchaintest.NewInterchain().
AddChain(provider).
AddChain(consumer).
AddRelayer(r, "relayer").
AddProviderConsumerLink(interchaintest.ProviderConsumerLink{
Provider: provider,
Consumer: consumer,
Relayer: r,
Path: ibcPath,
})
// Prep Interchain
const ibcPath = "ics-path"
ic := interchaintest.NewInterchain().
AddChain(provider).
AddChain(consumer).
AddRelayer(r, "relayer").
AddProviderConsumerLink(interchaintest.ProviderConsumerLink{
Provider: provider,
Consumer: consumer,
Relayer: r,
Path: ibcPath,
})

// Log location
f, err := interchaintest.CreateLogFile(fmt.Sprintf("%d.json", time.Now().Unix()))
require.NoError(t, err)
// Reporter/logs
rep := testreporter.NewReporter(f)
eRep := rep.RelayerExecReporter(t)
// Log location
f, err := interchaintest.CreateLogFile(fmt.Sprintf("%d.json", time.Now().Unix()))
require.NoError(t, err)
// Reporter/logs
rep := testreporter.NewReporter(f)
eRep := rep.RelayerExecReporter(t)

// Build interchain
err = ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{
TestName: t.Name(),
Client: client,
NetworkID: network,
BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(),
// Build interchain
err = ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{
TestName: t.Name(),
Client: client,
NetworkID: network,
BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(),

SkipPathCreation: false,
})
require.NoError(t, err, "failed to build interchain")
SkipPathCreation: false,
})
require.NoError(t, err, "failed to build interchain")

err = testutil.WaitForBlocks(ctx, 10, provider, consumer)
require.NoError(t, err, "failed to wait for blocks")
err = testutil.WaitForBlocks(ctx, 10, provider, consumer)
require.NoError(t, err, "failed to wait for blocks")
})
}
}
})
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ require (
go.etcd.io/bbolt v1.3.7 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/mod v0.12.0
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sys v0.16.0 // indirect
Expand Down
Loading