diff --git a/modules/core/02-client/client/cli/cli.go b/modules/core/02-client/client/cli/cli.go index 2490668f122..4b05f4dbac0 100644 --- a/modules/core/02-client/client/cli/cli.go +++ b/modules/core/02-client/client/cli/cli.go @@ -50,6 +50,7 @@ func NewTxCmd() *cobra.Command { newUpgradeClientCmd(), newSubmitRecoverClientProposalCmd(), newScheduleIBCUpgradeProposalCmd(), + newProvideCounterpartyCmd(), ) return txCmd diff --git a/modules/core/02-client/client/cli/tx.go b/modules/core/02-client/client/cli/tx.go index 223343dbfd9..8f4242ec6ca 100644 --- a/modules/core/02-client/client/cli/tx.go +++ b/modules/core/02-client/client/cli/tx.go @@ -1,9 +1,11 @@ package cli import ( + "encoding/hex" "fmt" "os" "strconv" + "strings" "github.com/spf13/cobra" @@ -20,6 +22,7 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" + commitmenttypesv2 "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types/v2" "github.com/cosmos/ibc-go/v9/modules/core/exported" ) @@ -450,3 +453,54 @@ func newScheduleIBCUpgradeProposalCmd() *cobra.Command { return cmd } + +// newProvideCounterpartyCmd defines the command to provide the counterparty to an IBC client. +func newProvideCounterpartyCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "provide-counterparty [client-identifier] [counterparty-client-identifier] [counterparty-merkle-path-prefix]", + Args: cobra.ExactArgs(3), + Short: "provide the counterparty to an IBC client", + Long: `Provide the counterparty to an IBC client specified by its client ID. +The [counterparty-merkle-path-prefix] is a comma-separated list of hex-encoded strings.`, + Example: fmt.Sprintf("%s tx %s %s provide-counterparty 07-tendermint-0 07-tendermint-1 696263,657572656b61", version.AppName, exported.ModuleName, types.SubModuleName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + clientIdentifier := args[0] + counterpartyClientIdentifier := args[1] + counterpartyMerklePathPrefix, err := parseMerklePathPrefix(args[2]) + if err != nil { + return err + } + + counterparty := types.NewCounterparty(counterpartyClientIdentifier, counterpartyMerklePathPrefix) + msg := types.MsgProvideCounterparty{ + ClientId: clientIdentifier, + Counterparty: counterparty, + Signer: clientCtx.GetFromAddress().String(), + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} + +// parseMerklePathPrefix parses a comma-separated list of hex-encoded strings into a MerklePath. +func parseMerklePathPrefix(merklePathPrefixString string) (commitmenttypesv2.MerklePath, error) { + var keyPath [][]byte + hexPrefixes := strings.Split(merklePathPrefixString, ",") + for _, hexPrefix := range hexPrefixes { + prefix, err := hex.DecodeString(hexPrefix) + if err != nil { + return commitmenttypesv2.MerklePath{}, fmt.Errorf("invalid hex merkle path prefix: %w", err) + } + keyPath = append(keyPath, prefix) + } + + return commitmenttypesv2.MerklePath{KeyPath: keyPath}, nil +}