Skip to content

Commit

Permalink
ibc: flexible channel handshake selection (#7439)
Browse files Browse the repository at this point in the history
* update channel handshake

Add ProvedID to ChanOpenTry which allows for flexible handshake identifier selection. Add CounterpartyChannelID to OpenAck. Update and add to handshake tests. Modify msgs_test. Counterparty ValidateBasic will return nil on an empty channel identifier to allow for flexible handshake identifier selection to succeed.

* Update x/ibc/core/04-channel/keeper/handshake.go

* add conn open init docs

* update docs for conn open try and conn open ack

* update channel docs
  • Loading branch information
colin-axner authored Oct 2, 2020
1 parent 82c9ae3 commit 0047099
Show file tree
Hide file tree
Showing 14 changed files with 449 additions and 218 deletions.
24 changes: 13 additions & 11 deletions proto/ibc/core/channel/v1/channel.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ message MsgChannelOpenTry {

string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""];
Channel channel = 3 [(gogoproto.nullable) = false];
string counterparty_version = 4 [(gogoproto.moretags) = "yaml:\"counterparty_version\""];
bytes proof_init = 5 [(gogoproto.moretags) = "yaml:\"proof_init\""];
ibc.core.client.v1.Height proof_height = 6
string proved_channel_id = 3 [(gogoproto.moretags) = "yaml:\"proved_channel_id\""];
Channel channel = 4 [(gogoproto.nullable) = false];
string counterparty_version = 5 [(gogoproto.moretags) = "yaml:\"counterparty_version\""];
bytes proof_init = 6 [(gogoproto.moretags) = "yaml:\"proof_init\""];
ibc.core.client.v1.Height proof_height = 7
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
string signer = 7;
string signer = 8;
}

// MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge
Expand All @@ -40,13 +41,14 @@ message MsgChannelOpenAck {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""];
string counterparty_version = 3 [(gogoproto.moretags) = "yaml:\"counterparty_version\""];
bytes proof_try = 4 [(gogoproto.moretags) = "yaml:\"proof_try\""];
ibc.core.client.v1.Height proof_height = 5
string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""];
string counterparty_channel_id = 3 [(gogoproto.moretags) = "yaml:\"counterparty_channel_id\""];
string counterparty_version = 4 [(gogoproto.moretags) = "yaml:\"counterparty_version\""];
bytes proof_try = 5 [(gogoproto.moretags) = "yaml:\"proof_try\""];
ibc.core.client.v1.Height proof_height = 6
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
string signer = 6;
string signer = 7;
}

// MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to
Expand Down
7 changes: 3 additions & 4 deletions x/ibc/core/03-connection/keeper/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() {
// retrieve client state of chainA to pass as counterpartyClient
counterpartyClient = suite.chainA.GetClientState(clientA)
}, true},
{"success with empty provedID", func() {
{"invalid provedID", func() {
clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, ibctesting.Tendermint)
connA, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB)
suite.Require().NoError(err)
Expand Down Expand Up @@ -256,7 +256,6 @@ func (suite *KeeperTestSuite) TestConnOpenTry() {

// retrieve client state of chainA to pass as counterpartyClient
counterpartyClient = suite.chainA.GetClientState(clientA)

}, false},
{"invalid previous connection has invalid versions", func() {
clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, ibctesting.Tendermint)
Expand Down Expand Up @@ -364,7 +363,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() {
// retrieve client state of chainB to pass as counterpartyClient
counterpartyClient = suite.chainB.GetClientState(clientB)
}, true},
{"success with empty counterparty connection ID", func() {
{"success with empty stored counterparty connection ID", func() {
clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, ibctesting.Tendermint)
connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB)
suite.Require().NoError(err)
Expand Down Expand Up @@ -412,7 +411,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() {
// retrieve client state of chainB to pass as counterpartyClient
counterpartyClient = suite.chainB.GetClientState(clientB)
}, true},
{"success from tryopen with empty string", func() {
{"success from tryopen with empty stored connection id", func() {
// chainA is in TRYOPEN, chainB is in TRYOPEN
clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, ibctesting.Tendermint)
connB, connA, err := suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA)
Expand Down
28 changes: 15 additions & 13 deletions x/ibc/core/04-channel/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ func NewChannelOpenInitCmd() *cobra.Command {
// NewChannelOpenTryCmd returns the command to create a MsgChannelOpenTry transaction
func NewChannelOpenTryCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "open-try [port-id] [channel-id] [counterparty-port-id] [counterparty-channel-id] [connection-hops] [/path/to/proof_init.json] [proof-height]",
Use: "open-try [port-id] [channel-id] [proved-channel-id] [counterparty-port-id] [counterparty-channel-id] [connection-hops] [/path/to/proof_init.json] [proof-height]",
Short: "Creates and sends a ChannelOpenTry message",
Args: cobra.ExactArgs(7),
Args: cobra.ExactArgs(8),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
Expand All @@ -76,26 +76,27 @@ func NewChannelOpenTryCmd() *cobra.Command {

portID := args[0]
channelID := args[1]
counterpartyPortID := args[2]
counterpartyChannelID := args[3]
hops := strings.Split(args[4], "/")
provedChannelID := args[2]
counterpartyPortID := args[3]
counterpartyChannelID := args[4]
hops := strings.Split(args[5], "/")
order := channelOrder(cmd.Flags())

// TODO: Differentiate between channel and counterparty versions.
version, _ := cmd.Flags().GetString(FlagIBCVersion)

proofInit, err := connectionutils.ParseProof(clientCtx.LegacyAmino, args[5])
proofInit, err := connectionutils.ParseProof(clientCtx.LegacyAmino, args[6])
if err != nil {
return err
}

proofHeight, err := clienttypes.ParseHeight(args[6])
proofHeight, err := clienttypes.ParseHeight(args[7])
if err != nil {
return err
}

msg := types.NewMsgChannelOpenTry(
portID, channelID, version, order, hops,
portID, channelID, provedChannelID, version, order, hops,
counterpartyPortID, counterpartyChannelID, version,
proofInit, proofHeight, clientCtx.GetFromAddress(),
)
Expand All @@ -117,9 +118,9 @@ func NewChannelOpenTryCmd() *cobra.Command {
// NewChannelOpenAckCmd returns the command to create a MsgChannelOpenAck transaction
func NewChannelOpenAckCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "open-ack [port-id] [channel-id] [/path/to/proof_try.json] [proof-height]",
Use: "open-ack [port-id] [channel-id] [counterparty-channel-id] [/path/to/proof_try.json] [proof-height]",
Short: "Creates and sends a ChannelOpenAck message",
Args: cobra.ExactArgs(4),
Args: cobra.ExactArgs(5),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
Expand All @@ -129,22 +130,23 @@ func NewChannelOpenAckCmd() *cobra.Command {

portID := args[0]
channelID := args[1]
counterpartyChannelID := args[2]

// TODO: Differentiate between channel and counterparty versions.
version, _ := cmd.Flags().GetString(FlagIBCVersion)

proofTry, err := connectionutils.ParseProof(clientCtx.LegacyAmino, args[2])
proofTry, err := connectionutils.ParseProof(clientCtx.LegacyAmino, args[3])
if err != nil {
return err
}

proofHeight, err := clienttypes.ParseHeight(args[3])
proofHeight, err := clienttypes.ParseHeight(args[4])
if err != nil {
return err
}

msg := types.NewMsgChannelOpenAck(
portID, channelID, version, proofTry, proofHeight, clientCtx.GetFromAddress(),
portID, channelID, counterpartyChannelID, version, proofTry, proofHeight, clientCtx.GetFromAddress(),
)
if err := msg.ValidateBasic(); err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions x/ibc/core/04-channel/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func HandleMsgChannelOpenInit(ctx sdk.Context, k keeper.Keeper, portCap *capabil

// HandleMsgChannelOpenTry defines the sdk.Handler for MsgChannelOpenTry
func HandleMsgChannelOpenTry(ctx sdk.Context, k keeper.Keeper, portCap *capabilitytypes.Capability, msg *types.MsgChannelOpenTry) (*sdk.Result, *capabilitytypes.Capability, error) {
capKey, err := k.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, msg.ChannelId,
capKey, err := k.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, msg.ChannelId, msg.ProvedChannelId,
portCap, msg.Channel.Counterparty, msg.Channel.Version, msg.CounterpartyVersion, msg.ProofInit, msg.ProofHeight,
)
if err != nil {
Expand Down Expand Up @@ -70,7 +70,7 @@ func HandleMsgChannelOpenTry(ctx sdk.Context, k keeper.Keeper, portCap *capabili
// HandleMsgChannelOpenAck defines the sdk.Handler for MsgChannelOpenAck
func HandleMsgChannelOpenAck(ctx sdk.Context, k keeper.Keeper, channelCap *capabilitytypes.Capability, msg *types.MsgChannelOpenAck) (*sdk.Result, error) {
err := k.ChanOpenAck(
ctx, msg.PortId, msg.ChannelId, channelCap, msg.CounterpartyVersion, msg.ProofTry, msg.ProofHeight,
ctx, msg.PortId, msg.ChannelId, channelCap, msg.CounterpartyVersion, msg.CounterpartyChannelId, msg.ProofTry, msg.ProofHeight,
)
if err != nil {
return nil, sdkerrors.Wrap(err, "channel handshake open ack failed")
Expand Down
29 changes: 25 additions & 4 deletions x/ibc/core/04-channel/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ func (k Keeper) ChanOpenTry(
order types.Order,
connectionHops []string,
portID,
channelID string,
channelID,
provedChannelID string,
portCap *capabilitytypes.Capability,
counterparty types.Counterparty,
version,
Expand Down Expand Up @@ -147,6 +148,15 @@ func (k Keeper) ChanOpenTry(
)
}

// empty-string is a sentinel value for "allow any identifier" to be selected by
// the counterparty channel
if provedChannelID != channelID && provedChannelID != "" {
return nil, sdkerrors.Wrapf(
types.ErrInvalidChannelIdentifier,
"proved channel identifier (%s) must equal channel identifier (%s) or be empty", provedChannelID, channelID,
)
}

// NOTE: this step has been switched with the one below to reverse the connection
// hops
channel := types.NewChannel(types.TRYOPEN, order, counterparty, connectionHops, version)
Expand All @@ -159,7 +169,7 @@ func (k Keeper) ChanOpenTry(

// expectedCounterpaty is the counterparty of the counterparty's channel end
// (i.e self)
expectedCounterparty := types.NewCounterparty(portID, channelID)
expectedCounterparty := types.NewCounterparty(portID, provedChannelID)
expectedChannel := types.NewChannel(
types.INIT, channel.Ordering, expectedCounterparty,
counterpartyHops, counterpartyVersion,
Expand Down Expand Up @@ -194,7 +204,8 @@ func (k Keeper) ChanOpenAck(
portID,
channelID string,
chanCap *capabilitytypes.Capability,
counterpartyVersion string,
counterpartyVersion,
counterpartyChannelID string,
proofTry []byte,
proofHeight exported.Height,
) error {
Expand All @@ -214,6 +225,15 @@ func (k Keeper) ChanOpenAck(
return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID)
}

// empty-string is a sentinel value for "allow any identifier" to be selected by
// the counterparty channel
if counterpartyChannelID != channel.Counterparty.ChannelId && channel.Counterparty.ChannelId != "" {
return sdkerrors.Wrapf(
types.ErrInvalidChannelIdentifier,
"counterparty channel identifier (%s) must be empty or equal to stored channel ID for counterparty (%s)", counterpartyChannelID, channel.Counterparty.ChannelId,
)
}

connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
if !found {
return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0])
Expand Down Expand Up @@ -241,7 +261,7 @@ func (k Keeper) ChanOpenAck(

if err := k.connectionKeeper.VerifyChannelState(
ctx, connectionEnd, proofHeight, proofTry,
channel.Counterparty.PortId, channel.Counterparty.ChannelId,
channel.Counterparty.PortId, counterpartyChannelID,
expectedChannel,
); err != nil {
return err
Expand All @@ -251,6 +271,7 @@ func (k Keeper) ChanOpenAck(

channel.State = types.OPEN
channel.Version = counterpartyVersion
channel.Counterparty.ChannelId = counterpartyChannelID
k.SetChannel(ctx, portID, channelID, channel)

return nil
Expand Down
Loading

0 comments on commit 0047099

Please sign in to comment.