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: remove crossing hellos from 03-connection #1672

Merged
merged 10 commits into from
Jul 12, 2022
2 changes: 1 addition & 1 deletion docs/ibc/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -4244,7 +4244,7 @@ connection on Chain B.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `client_id` | [string](#string) | | |
| `previous_connection_id` | [string](#string) | | in the case of crossing hello's, when both chains call OpenInit, we need the connection identifier of the previous connection in state INIT |
| `previous_connection_id` | [string](#string) | | **Deprecated.** Deprecated: this field is unused. Crossing hellos are no longer supported in core IBC. |
| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | |
| `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | |
| `delay_period` | [uint64](#uint64) | | |
Expand Down
5 changes: 5 additions & 0 deletions docs/migrations/v3-to-v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ The `NewErrorAcknowledgement` method signature has changed.
It now accepts an `error` rather than a `string`. This was done in order to prevent accidental state changes.
All error acknowledgements now contain a deterministic ABCI code and error message. It is the responsibility of the application developer to emit error details in events.

Crossing hellos have been removed from 03-connection handshake negotiation.
Copy link
Contributor

@crodriguezvega crodriguezvega Jul 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be added to a different section like ### ICS03 - Connection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

`PreviousConnectionId` in `MsgConnectionOpenTry` has been deprecated and is no longer used by core IBC.

### ICS27 - Interchain Accounts

The `RegisterInterchainAccount` API has been modified to include an additional `version` argument. This change has been made in order to support ICS29 fee middleware, for relayer incentivization of ICS27 packets.
Expand Down Expand Up @@ -91,3 +94,5 @@ if err := k.icaControllerKeeper.RegisterInterchainAccount(ctx, msg.ConnectionId,
## Relayers

When using the `DenomTrace` gRPC, the full IBC denomination with the `ibc/` prefix may now be passed in.

Crossing hellos are no longer supported by core IBC for 03-connection and 04-channel. The handshake should be completed in the logical 4 step process (INIT, TRY, ACK, CONFIRM).
79 changes: 13 additions & 66 deletions modules/core/03-connection/keeper/handshake.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package keeper

import (
"bytes"

"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/gogo/protobuf/proto"

clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
"github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"
Expand All @@ -28,7 +25,7 @@ func (k Keeper) ConnOpenInit(
) (string, error) {
versions := types.GetCompatibleVersions()
if version != nil {
if !types.IsSupportedVersion(version) {
if !types.IsSupportedVersion(types.GetCompatibleVersions(), version) {
return "", sdkerrors.Wrap(types.ErrInvalidVersion, "version is not supported")
}

Expand Down Expand Up @@ -63,7 +60,6 @@ func (k Keeper) ConnOpenInit(
// - Identifiers are checked on msg validation
func (k Keeper) ConnOpenTry(
ctx sdk.Context,
previousConnectionID string, // previousIdentifier
counterparty types.Counterparty, // counterpartyConnectionIdentifier, counterpartyPrefix and counterpartyClientIdentifier
delayPeriod uint64,
clientID string, // clientID of chainA
Expand All @@ -75,44 +71,9 @@ func (k Keeper) ConnOpenTry(
proofHeight exported.Height, // height at which relayer constructs proof of A storing connectionEnd in state
consensusHeight exported.Height, // latest height of chain B which chain A has stored in its chain B client
) (string, error) {
var (
connectionID string
previousConnection types.ConnectionEnd
found bool
)

// empty connection identifier indicates continuing a previous connection handshake
if previousConnectionID != "" {
// ensure that the previous connection exists
previousConnection, found = k.GetConnection(ctx, previousConnectionID)
if !found {
return "", sdkerrors.Wrapf(types.ErrConnectionNotFound, "previous connection does not exist for supplied previous connectionID %s", previousConnectionID)
}

// ensure that the existing connection's
// counterparty is chainA and connection is on INIT stage.
// Check that existing connection versions for initialized connection is equal to compatible
// versions for this chain.
// ensure that existing connection's delay period is the same as desired delay period.
if !(previousConnection.Counterparty.ConnectionId == "" &&
bytes.Equal(previousConnection.Counterparty.Prefix.Bytes(), counterparty.Prefix.Bytes()) &&
previousConnection.ClientId == clientID &&
previousConnection.Counterparty.ClientId == counterparty.ClientId &&
previousConnection.DelayPeriod == delayPeriod) {
return "", sdkerrors.Wrap(types.ErrInvalidConnection, "connection fields mismatch previous connection fields")
}

if !(previousConnection.State == types.INIT) {
return "", sdkerrors.Wrapf(types.ErrInvalidConnectionState, "previous connection state is in state %s, expected INIT", previousConnection.State)
}

// continue with previous connection
connectionID = previousConnectionID

} else {
// generate a new connection
connectionID = k.GenerateConnectionIdentifier(ctx)
}
// generate a new connection
connectionID := k.GenerateConnectionIdentifier(ctx)

selfHeight := clienttypes.GetSelfHeight(ctx)
if consensusHeight.GTE(selfHeight) {
Expand All @@ -139,15 +100,10 @@ func (k Keeper) ConnOpenTry(
expectedCounterparty := types.NewCounterparty(clientID, "", commitmenttypes.NewMerklePrefix(prefix.Bytes()))
expectedConnection := types.NewConnectionEnd(types.INIT, counterparty.ClientId, expectedCounterparty, types.ExportedVersionsToProto(counterpartyVersions), delayPeriod)

supportedVersions := types.GetCompatibleVersions()
if len(previousConnection.Versions) != 0 {
supportedVersions = previousConnection.GetVersions()
}

// chain B picks a version from Chain A's available versions that is compatible
// with Chain B's supported IBC versions. PickVersion will select the intersection
// of the supported versions and the counterparty versions.
version, err := types.PickVersion(supportedVersions, counterpartyVersions)
version, err := types.PickVersion(types.GetCompatibleVersions(), counterpartyVersions)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -181,7 +137,7 @@ func (k Keeper) ConnOpenTry(
}

k.SetConnection(ctx, connectionID, connection)
k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", previousConnection.State.String(), "new-state", "TRYOPEN")
k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "NONE", "new-state", "TRYOPEN")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same as previous comment!

Suggested change
k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "NONE", "new-state", "TRYOPEN")
k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "UNINITIALIZED", "new-state", "TRYOPEN")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the INIT functions have NONE as well. Maybe we should make all the changes in one separate pr?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, whichever you prefer! I'm easy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


defer func() {
telemetry.IncrCounter(1, "ibc", "connection", "open-try")
Expand Down Expand Up @@ -223,28 +179,19 @@ func (k Keeper) ConnOpenAck(
return sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID)
}

// Verify the provided version against the previously set connection state
switch {
// connection on ChainA must be in INIT or TRYOPEN
case connection.State != types.INIT && connection.State != types.TRYOPEN:
return sdkerrors.Wrapf(
types.ErrInvalidConnectionState,
"connection state is not INIT or TRYOPEN (got %s)", connection.State.String(),
)

// if the connection is INIT then the provided version must be supproted
case connection.State == types.INIT && !types.IsSupportedVersion(version):
// verify the previously set connection state
if connection.State != types.INIT {
return sdkerrors.Wrapf(
types.ErrInvalidConnectionState,
"connection state is in INIT but the provided version is not supported %s", version,
"connection state is not INIT (got %s)", connection.State.String(),
)
}

// if the connection is in TRYOPEN then the version must be the only set version in the
// retreived connection state.
case connection.State == types.TRYOPEN && (len(connection.Versions) != 1 || !proto.Equal(connection.Versions[0], version)):
// ensure selected version is supported
if !types.IsSupportedVersion(types.ProtoVersionsToExported(connection.Versions), version) {
Copy link
Contributor Author

@colin-axner colin-axner Jul 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be possible to change the API's to use the proto version type (remove the switch between exported/proto). We would remove the GetVersions function from the connection interface which is fine since we only have one connection type (we can cast to connectiontypes.ConnectionEnd) which is already what occurs

Lets open an issue and do in a followup pr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opened #1689 and #1690

return sdkerrors.Wrapf(
types.ErrInvalidConnectionState,
"connection state is in TRYOPEN but the provided version (%s) is not set in the previous connection versions %s", version, connection.Versions,
"the selected version is not supported %s", version,
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
)
}

Expand Down Expand Up @@ -283,7 +230,7 @@ func (k Keeper) ConnOpenAck(
return err
}

k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", connection.State.String(), "new-state", "OPEN")
k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "INIT", "new-state", "OPEN")

defer func() {
telemetry.IncrCounter(1, "ibc", "connection", "open-ack")
Expand Down
70 changes: 6 additions & 64 deletions modules/core/03-connection/keeper/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,11 @@ func (suite *KeeperTestSuite) TestConnOpenInit() {
// connection on chainA is INIT
func (suite *KeeperTestSuite) TestConnOpenTry() {
var (
path *ibctesting.Path
delayPeriod uint64
previousConnectionID string
versions []exported.Version
consensusHeight exported.Height
counterpartyClient exported.ClientState
path *ibctesting.Path
delayPeriod uint64
versions []exported.Version
consensusHeight exported.Height
counterpartyClient exported.ClientState
)

testCases := []struct {
Expand All @@ -100,15 +99,6 @@ func (suite *KeeperTestSuite) TestConnOpenTry() {
// retrieve client state of chainA to pass as counterpartyClient
counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID)
}, true},
{"success with crossing hellos", func() {
err := suite.coordinator.ConnOpenInitOnBothChains(path)
suite.Require().NoError(err)

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

previousConnectionID = path.EndpointB.ConnectionID
}, true},
{"success with delay period", func() {
err := path.EndpointA.ConnOpenInit()
suite.Require().NoError(err)
Expand Down Expand Up @@ -227,8 +217,6 @@ func (suite *KeeperTestSuite) TestConnOpenTry() {

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

previousConnectionID = path.EndpointB.ConnectionID
}, false},
{"invalid previous connection has invalid versions", func() {
// open init chainA
Expand All @@ -253,8 +241,6 @@ func (suite *KeeperTestSuite) TestConnOpenTry() {

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

previousConnectionID = path.EndpointB.ConnectionID
}, false},
}

Expand All @@ -265,7 +251,6 @@ func (suite *KeeperTestSuite) TestConnOpenTry() {
suite.SetupTest() // reset
consensusHeight = clienttypes.ZeroHeight() // must be explicitly changed in malleate
versions = types.GetCompatibleVersions() // must be explicitly changed in malleate
previousConnectionID = ""
path = ibctesting.NewPath(suite.chainA, suite.chainB)
suite.coordinator.SetupClients(path)

Expand All @@ -292,7 +277,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() {
proofClient, _ := suite.chainA.QueryProof(clientKey)

connectionID, err := suite.chainB.App.GetIBCKeeper().ConnectionKeeper.ConnOpenTry(
suite.chainB.GetContext(), previousConnectionID, counterparty, delayPeriod, path.EndpointB.ClientID, counterpartyClient,
suite.chainB.GetContext(), counterparty, delayPeriod, path.EndpointB.ClientID, counterpartyClient,
versions, proofInit, proofClient, proofConsensus,
proofHeight, consensusHeight,
)
Expand Down Expand Up @@ -333,27 +318,6 @@ func (suite *KeeperTestSuite) TestConnOpenAck() {
// retrieve client state of chainB to pass as counterpartyClient
counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID)
}, true},
{"success from tryopen", func() {
// chainA is in TRYOPEN, chainB is in TRYOPEN
err := path.EndpointB.ConnOpenInit()
suite.Require().NoError(err)

err = path.EndpointA.ConnOpenTry()
suite.Require().NoError(err)

// set chainB to TRYOPEN
connection := path.EndpointB.GetConnection()
connection.State = types.TRYOPEN
connection.Counterparty.ConnectionId = path.EndpointA.ConnectionID
suite.chainB.App.GetIBCKeeper().ConnectionKeeper.SetConnection(suite.chainB.GetContext(), path.EndpointB.ConnectionID, connection)
// update path.EndpointB.ClientID so state change is committed
path.EndpointB.UpdateClient()

path.EndpointA.UpdateClient()

// retrieve client state of chainB to pass as counterpartyClient
counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID)
}, true},
{"invalid counterparty client", func() {
err := path.EndpointA.ConnOpenInit()
suite.Require().NoError(err)
Expand Down Expand Up @@ -440,28 +404,6 @@ func (suite *KeeperTestSuite) TestConnOpenAck() {

version = types.NewVersion("2.0", nil)
}, false},
{"connection is in TRYOPEN but the set version in the connection is invalid", func() {
// chainA is in TRYOPEN, chainB is in TRYOPEN
err := path.EndpointB.ConnOpenInit()
suite.Require().NoError(err)

err = path.EndpointA.ConnOpenTry()
suite.Require().NoError(err)

// set chainB to TRYOPEN
connection := path.EndpointB.GetConnection()
connection.State = types.TRYOPEN
suite.chainB.App.GetIBCKeeper().ConnectionKeeper.SetConnection(suite.chainB.GetContext(), path.EndpointB.ConnectionID, connection)

// update path.EndpointB.ClientID so state change is committed
path.EndpointB.UpdateClient()
path.EndpointA.UpdateClient()

// retrieve client state of chainB to pass as counterpartyClient
counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID)

version = types.NewVersion("2.0", nil)
}, false},
{"incompatible IBC versions", func() {
err := path.EndpointA.ConnOpenInit()
suite.Require().NoError(err)
Expand Down
Loading