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

Modify Connection Open Init to allow a selected version #7431

Closed
wants to merge 9 commits into from
Closed
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
3 changes: 2 additions & 1 deletion proto/ibc/connection/connection.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ message MsgConnectionOpenInit {
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""];
Counterparty counterparty = 3 [(gogoproto.nullable) = false];
string signer = 4;
string version = 4;
string signer = 5;
}

// MsgConnectionOpenTry defines a msg sent by a Relayer to try to open a
Expand Down
35 changes: 32 additions & 3 deletions x/ibc/03-connection/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,22 @@ import (
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)

const (
flagVersionIdentifier = "version-identifier"
flagVersionFeatures = "version-features"
)

// NewConnectionOpenInitCmd defines the command to initialize a connection on
// chain A with a given counterparty chain B
func NewConnectionOpenInitCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "open-init [connection-id] [client-id] [counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json]",
Short: "Initialize connection on chain A",
Long: "Initialize a connection on chain A with a given counterparty chain B",
Long: `Initialize a connection on chain A with a given counterparty chain B.
- 'version-identifier' flag can be a single pre-selected version identifier to be used in the handshake.
- 'version-features' flag can be a list of features separated by commas to accompany the version identifier.`,
Example: fmt.Sprintf(
"%s tx %s %s open-init [connection-id] [client-id] [counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json]",
"%s tx %s %s open-init [connection-id] [client-id] [counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json] --version-identifier=\"1.0\" --version-features=\"ORDER_UNORDERED\"",
version.AppName, host.ModuleName, types.SubModuleName,
),
Args: cobra.ExactArgs(5),
Expand All @@ -45,9 +52,27 @@ func NewConnectionOpenInitCmd() *cobra.Command {
return err
}

var encodedVersion string
versionIdentifier, _ := cmd.Flags().GetString(flagVersionIdentifier)

if versionIdentifier != "" {
var features []string

versionFeatures, _ := cmd.Flags().GetString(flagVersionFeatures)
if versionFeatures != "" {
features = strings.Split(versionFeatures, ",")
}

version := types.NewVersion(versionIdentifier, features)
encodedVersion, err = version.Encode()
if err != nil {
return err
}
}

msg := types.NewMsgConnectionOpenInit(
connectionID, clientID, counterpartyConnectionID, counterpartyClientID,
counterpartyPrefix, clientCtx.GetFromAddress(),
counterpartyPrefix, encodedVersion, clientCtx.GetFromAddress(),
)

if err := msg.ValidateBasic(); err != nil {
Expand All @@ -58,6 +83,10 @@ func NewConnectionOpenInitCmd() *cobra.Command {
},
}

// NOTE: we should use empty default values since the user may not want to select a version
// at this step in the handshake.
cmd.Flags().String(flagVersionIdentifier, "", "version identifier to be used in the connection handshake version negotiation")
cmd.Flags().String(flagVersionFeatures, "", "version features list separated by commas without spaces. The features must function with the version identifier.")
flags.AddTxFlagsToCmd(cmd)

return cmd
Expand Down
2 changes: 1 addition & 1 deletion x/ibc/03-connection/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
// HandleMsgConnectionOpenInit defines the sdk.Handler for MsgConnectionOpenInit
func HandleMsgConnectionOpenInit(ctx sdk.Context, k keeper.Keeper, msg *types.MsgConnectionOpenInit) (*sdk.Result, error) {
if err := k.ConnOpenInit(
ctx, msg.ConnectionId, msg.ClientId, msg.Counterparty,
ctx, msg.ConnectionId, msg.ClientId, msg.Counterparty, msg.Version,
); err != nil {
return nil, sdkerrors.Wrap(err, "connection handshake open init failed")
}
Expand Down
12 changes: 11 additions & 1 deletion x/ibc/03-connection/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,24 @@ func (k Keeper) ConnOpenInit(
connectionID, // identifier
clientID string,
counterparty types.Counterparty, // desiredCounterpartyConnectionIdentifier, counterpartyPrefix, counterpartyClientIdentifier
version string,
) error {
_, found := k.GetConnection(ctx, connectionID)
if found {
return types.ErrConnectionExists
}

versions := types.GetCompatibleEncodedVersions()
if version != "" {
if !types.IsSupportedVersion(version) {
return sdkerrors.Wrap(types.ErrInvalidVersion, "version is not supported")
}

versions = []string{version}
}

// connection defines chain A's ConnectionEnd
connection := types.NewConnectionEnd(types.INIT, clientID, counterparty, types.GetCompatibleEncodedVersions())
connection := types.NewConnectionEnd(types.INIT, clientID, counterparty, versions)
k.SetConnection(ctx, connectionID, connection)

if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil {
Expand Down
12 changes: 11 additions & 1 deletion x/ibc/03-connection/keeper/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (suite *KeeperTestSuite) TestConnOpenInit() {
var (
clientA string
clientB string
version string
)

testCases := []struct {
Expand All @@ -27,9 +28,17 @@ func (suite *KeeperTestSuite) TestConnOpenInit() {
{"success", func() {
clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, ibctesting.Tendermint)
}, true},
{"success with non empty version", func() {
clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, ibctesting.Tendermint)
version = types.GetCompatibleEncodedVersions()[0]
}, true},
{"connection already exists", func() {
clientA, clientB, _, _ = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, ibctesting.Tendermint)
}, false},
{"invalid version", func() {
clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, ibctesting.Tendermint)
version = "bad version"
}, false},
{"couldn't add connection to client", func() {
// swap client identifiers to result in client that does not exist
clientB, clientA = suite.coordinator.SetupClients(suite.chainA, suite.chainB, ibctesting.Tendermint)
Expand All @@ -40,14 +49,15 @@ func (suite *KeeperTestSuite) TestConnOpenInit() {
tc := tc
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
version = "" // must be explicitly changed

tc.malleate()

connA := suite.chainA.GetFirstTestConnection(clientA, clientB)
connB := suite.chainB.GetFirstTestConnection(clientB, clientA)
counterparty := types.NewCounterparty(clientB, connB.ID, suite.chainB.GetPrefix())

err := suite.chainA.App.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.chainA.GetContext(), connA.ID, clientA, counterparty)
err := suite.chainA.App.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.chainA.GetContext(), connA.ID, clientA, counterparty, version)

if tc.expPass {
suite.Require().NoError(err)
Expand Down
Loading