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

Standardize connection versioning + channel versioning docs #6640

Merged
merged 37 commits into from
Jul 14, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4e0f898
update connection versions with feature set flag
colin-axner Jul 8, 2020
c7b7e42
make connection version modular to support channel versioning and reg…
colin-axner Jul 8, 2020
7dd7d56
revert IBCVersion renaming, add channel versioning logic
colin-axner Jul 8, 2020
b69d9c5
merge master
colin-axner Jul 8, 2020
4b8989c
fix channel version flag
colin-axner Jul 8, 2020
17bed09
remove unnecessary godoc
colin-axner Jul 8, 2020
7176684
remove unused func
colin-axner Jul 8, 2020
1baea76
fix lint and version test
colin-axner Jul 8, 2020
83a54e9
add test and fix error
colin-axner Jul 8, 2020
35a6e3e
Merge branch 'master' into colin/5846-channel-versioning
colin-axner Jul 8, 2020
f6d58c1
revert changes
colin-axner Jul 8, 2020
e0183f9
Merge branch 'colin/5846-channel-versioning' of github.com:cosmos/cos…
colin-axner Jul 8, 2020
c864d6f
update docs
colin-axner Jul 8, 2020
608866e
Merge branch 'master' into colin/5846-channel-versioning
colin-axner Jul 8, 2020
5e1e6f9
remove unnecessary godoc
colin-axner Jul 8, 2020
26148ca
Merge branch 'colin/5846-channel-versioning' of github.com:cosmos/cos…
colin-axner Jul 8, 2020
a513032
Apply suggestions from code review
colin-axner Jul 9, 2020
a7bd313
update doc
colin-axner Jul 9, 2020
ccff9de
add test cases for unchecked lines
colin-axner Jul 9, 2020
097e7ee
Merge branch 'colin/5846-channel-versioning' of github.com:cosmos/cos…
colin-axner Jul 9, 2020
3a60d37
Merge branch 'master' of github.com:cosmos/cosmos-sdk into colin/5846…
colin-axner Jul 9, 2020
54b95f5
go fmt
colin-axner Jul 9, 2020
5303fc4
begin migration to standardized version
colin-axner Jul 10, 2020
4ad50ab
revert proto changes
colin-axner Jul 13, 2020
dd1d31e
restructure versioning to go from string to proto
colin-axner Jul 13, 2020
e34c8dd
update versionStr to encodedVersion naming
colin-axner Jul 13, 2020
64611d6
fix version test build
colin-axner Jul 13, 2020
1a185e9
merge master
colin-axner Jul 13, 2020
b00df37
fix keeper tests
colin-axner Jul 13, 2020
f57fbcc
fix various tests
colin-axner Jul 13, 2020
c21ac65
fixes from self review
colin-axner Jul 13, 2020
0a9ba5e
update docs
colin-axner Jul 13, 2020
18d02fb
fix lint
colin-axner Jul 13, 2020
eed690b
add more code cov
colin-axner Jul 13, 2020
36c0012
rename ToString funcs to Encode/DecodeVersion + GetCompatibleEncodedV…
colin-axner Jul 13, 2020
59996b5
update spec docs
colin-axner Jul 14, 2020
0b49257
Merge branch 'master' into colin/5846-channel-versioning
colin-axner Jul 14, 2020
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
7 changes: 6 additions & 1 deletion x/ibc-transfer/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const (

// Version defines the current version the IBC tranfer
// module supports
Version = "ics20-1"
Version = "(ics20-1,[])"

// PortID is the default port id that transfer module binds to
PortID = "transfer"
Expand Down Expand Up @@ -45,3 +45,8 @@ func GetEscrowAddress(portID, channelID string) sdk.AccAddress {
func GetDenomPrefix(portID, channelID string) string {
return fmt.Sprintf("%s/%s/", portID, channelID)
}

// GetCompatibleVersions
func GetCompatibleVersions() []string {
return []string{Version}
}
11 changes: 4 additions & 7 deletions x/ibc/03-connection/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (k Keeper) ConnOpenTry(

// chain B picks a version from Chain A's available versions that is compatible
// with the supported IBC versions
version, err := types.PickVersion(counterpartyVersions)
version, err := types.PickVersion(types.GetCompatibleVersions(), counterpartyVersions, types.AllowNilFeatureSetMap)
if err != nil {
return err
}
Expand Down Expand Up @@ -156,7 +156,7 @@ func (k Keeper) ConnOpenAck(
)
}

// Check that ChainB's proposed version number is supported by chainA
// Check that ChainB's proposed version identifier is supported by chainA
supportedVersion, found := types.FindSupportedVersion(version, types.GetCompatibleVersions())
if !found {
return sdkerrors.Wrapf(
Expand All @@ -166,11 +166,8 @@ func (k Keeper) ConnOpenAck(
}

// Check that ChainB's proposed feature set is supported by chainA
if !types.VerifyProposedFeatureSet(version, supportedVersion) {
return sdkerrors.Wrapf(
types.ErrVersionNegotiationFailed,
"connection version feature set provided (%s) is not supported (%s)", version, types.GetCompatibleVersions(),
)
if err := types.VerifyProposedVersion(version, supportedVersion, types.AllowNilFeatureSetMap); err != nil {
return err
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
}

// Retrieve chainA's consensus state at consensusheight
Expand Down
2 changes: 1 addition & 1 deletion x/ibc/03-connection/types/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (c ConnectionEnd) ValidateBasic() error {
return sdkerrors.Wrap(sdkerrors.ErrInvalidVersion, "missing connection versions")
}
for _, version := range c.Versions {
if err := host.ConnectionVersionValidator(version); err != nil {
if err := host.VersionValidator(version); err != nil {
return err
}
}
Expand Down
2 changes: 1 addition & 1 deletion x/ibc/03-connection/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ var (
ErrInvalidCounterparty = sdkerrors.Register(SubModuleName, 7, "invalid counterparty connection")
ErrInvalidConnection = sdkerrors.Register(SubModuleName, 8, "invalid connection")
ErrInvalidVersion = sdkerrors.Register(SubModuleName, 9, "invalid connection version")
ErrVersionNegotiationFailed = sdkerrors.Register(SubModuleName, 10, "connection version negotiation failed")
ErrVersionNegotiationFailed = sdkerrors.Register(SubModuleName, 10, "version negotiation failed")
)
4 changes: 2 additions & 2 deletions x/ibc/03-connection/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (msg MsgConnectionOpenTry) ValidateBasic() error {
return sdkerrors.Wrap(sdkerrors.ErrInvalidVersion, "missing counterparty versions")
}
for _, version := range msg.CounterpartyVersions {
if err := host.ConnectionVersionValidator(version); err != nil {
if err := host.VersionValidator(version); err != nil {
return err
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
}
}
Expand Down Expand Up @@ -169,7 +169,7 @@ func (msg MsgConnectionOpenAck) ValidateBasic() error {
if err := host.ConnectionIdentifierValidator(msg.ConnectionID); err != nil {
return sdkerrors.Wrap(err, "invalid connection ID")
}
if err := host.ConnectionVersionValidator(msg.Version); err != nil {
if err := host.VersionValidator(msg.Version); err != nil {
return err
}
if len(msg.ProofTry) == 0 {
Expand Down
64 changes: 47 additions & 17 deletions x/ibc/03-connection/types/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,18 @@ import (
)

var (
// DefaultIBCVersion represents the latest supported version of IBC.
// The current version supports only ORDERED and UNORDERED channels.
// DefaultIBCVersion represents the latest supported version of IBC used
// in connection version negotiation. The current version supports only
// ORDERED and UNORDERED channels and requires at least one channel type
// to be agreed upon.
DefaultIBCVersion = CreateVersionString("1", []string{"ORDER_ORDERED", "ORDER_UNORDERED"})

// AllowNilFeatureSetMap is a helper map to indicate if a specified version
// is allowed to have a nil feature set. Any versions supported, but
// not included in the map default to not supporting nil feature sets.
AllowNilFeatureSetMap = map[string]bool{
DefaultIBCVersion: false,
}
)

// GetCompatibleVersions returns a descending ordered set of compatible IBC
Expand Down Expand Up @@ -39,7 +48,7 @@ func CreateVersionString(identifier string, featureSet []string) string {
// feature set of a version. An error is returned if the version is not valid.
func UnpackVersion(version string) (string, []string, error) {
// validate version so valid splitting assumptions can be made
if err := host.ConnectionVersionValidator(version); err != nil {
if err := host.VersionValidator(version); err != nil {
return "", nil, err
}

Expand Down Expand Up @@ -96,10 +105,8 @@ func FindSupportedVersion(version string, supportedVersions []string) (string, b
// set with the intersection of the features supported by the source and
// counterparty chains. This function is called in the ConnOpenTry handshake
// procedure.
func PickVersion(counterpartyVersions []string) (string, error) {
versions := GetCompatibleVersions()

for _, ver := range versions {
func PickVersion(supportedVersions, counterpartyVersions []string, allowNilFeatureSet map[string]bool) (string, error) {
for _, ver := range supportedVersions {
// check if the source version is supported by the counterparty
if counterpartyVer, found := FindSupportedVersion(ver, counterpartyVersions); found {
sourceIdentifier, sourceFeatures, err := UnpackVersion(ver)
Expand All @@ -113,6 +120,9 @@ func PickVersion(counterpartyVersions []string) (string, error) {
}

featureSet := GetFeatureSetIntersection(sourceFeatures, counterpartyFeatures)
if len(featureSet) == 0 && !allowNilFeatureSet[ver] {
continue
}

version := CreateVersionString(sourceIdentifier, featureSet)
return version, nil
Expand All @@ -121,7 +131,7 @@ func PickVersion(counterpartyVersions []string) (string, error) {

return "", sdkerrors.Wrapf(
ErrVersionNegotiationFailed,
"failed to find a matching counterparty version (%s) from the supported version list (%s)", counterpartyVersions, versions,
"failed to find a matching counterparty version (%s) from the supported version list (%s)", counterpartyVersions, supportedVersions,
)
}

Expand All @@ -139,26 +149,46 @@ func GetFeatureSetIntersection(sourceFeatureSet, counterpartyFeatureSet []string
return featureSet
}

// VerifyProposedFeatureSet verifies that the entire feature set in the
// proposed version is supported by this chain.
func VerifyProposedFeatureSet(proposedVersion, supportedVersion string) bool {
_, proposedFeatureSet, err := UnpackVersion(proposedVersion)
// VerifyProposedVersion verifies that the entire feature set in the
// proposed version is supported by this chain. If the feature set is
// empty it verifies that this is allowed for the specified version
// identifier. It also ensures that the supported version identifier
// matches the proposed version identifier.
func VerifyProposedVersion(proposedVersion, supportedVersion string, allowNilFeatureSet map[string]bool) error {
proposedIdentifier, proposedFeatureSet, err := UnpackVersion(proposedVersion)
if err != nil {
return false
return sdkerrors.Wrap(err, "could not unpack proposed version")
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, test case

}

_, supportedFeatureSet, err := UnpackVersion(supportedVersion)
if len(proposedFeatureSet) == 0 && allowNilFeatureSet != nil && !allowNilFeatureSet[proposedIdentifier] {
return sdkerrors.Wrapf(
ErrVersionNegotiationFailed,
"nil feature sets are not supported for version identifier (%s)", proposedIdentifier,
)
}

supportedIdentifier, supportedFeatureSet, err := UnpackVersion(supportedVersion)
if err != nil {
return false
return sdkerrors.Wrap(err, "could not unpack supported version")
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto

}

if supportedIdentifier != proposedIdentifier {
return sdkerrors.Wrapf(
ErrVersionNegotiationFailed,
"proposed version identifier by counterparty (%s) does not match supported version identifier (%s)", proposedIdentifier, supportedIdentifier,
)
}

for _, proposedFeature := range proposedFeatureSet {
if !contains(proposedFeature, supportedFeatureSet) {
return false
return sdkerrors.Wrapf(
ErrVersionNegotiationFailed,
"proposed feature set (%s) is not a supported feature set (%s)", proposedFeatureSet, supportedFeatureSet,
)
}
}

return true
return nil
}

// VerifySupportedFeature takes in a version string and feature string and returns
Expand Down
16 changes: 10 additions & 6 deletions x/ibc/03-connection/types/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ func TestPickVersion(t *testing.T) {
}{
{"valid default ibc version", types.GetCompatibleVersions(), types.DefaultIBCVersion, true},
{"valid version in counterparty versions", []string{"(version1,[])", "(2.0.0,[DAG,ZK])", types.DefaultIBCVersion}, types.DefaultIBCVersion, true},
{"valid identifier match but empty feature set", []string{"(1,[DAG,ORDERED-ZK,UNORDERED-zk])"}, "(1,[])", true},
{"valid identifier match but empty feature set not allowed", []string{"(1,[DAG,ORDERED-ZK,UNORDERED-zk])"}, "(1,[])", false},
{"empty counterparty versions", []string{}, "", false},
{"non-matching counterparty versions", []string{"(2.0.0,[])"}, "", false},
}

for i, tc := range testCases {
version, err := types.PickVersion(tc.counterpartyVersions)
version, err := types.PickVersion(types.GetCompatibleVersions(), tc.counterpartyVersions, types.AllowNilFeatureSetMap)

if tc.expPass {
require.NoError(t, err, "valid test case %d failed: %s", i, tc.name)
Expand All @@ -99,16 +99,20 @@ func TestVerifyProposedFeatureSet(t *testing.T) {
supportedVersion string
expPass bool
}{
{"entire feature set supported", types.DefaultIBCVersion, types.CreateVersionString("1", []string{"ORDER_ORDERED", "ORDER_UNORDERED", "ORDER_DAG"}), true},
{"empty feature sets", types.CreateVersionString("1", []string{}), types.DefaultIBCVersion, true},
{"entire feature set supported", types.DefaultIBCVersion, types.CreateVersionString("1", []string{"ORDER_ORDERED", "ORDER_UNORDERED", "DAG"}), true},
{"empty feature sets not supported", types.CreateVersionString("1", []string{}), types.DefaultIBCVersion, false},
{"one feature missing", types.DefaultIBCVersion, types.CreateVersionString("1", []string{"ORDER_UNORDERED", "ORDER_DAG"}), false},
{"both features missing", types.DefaultIBCVersion, types.CreateVersionString("1", []string{"ORDER_DAG"}), false},
}

for i, tc := range testCases {
supported := types.VerifyProposedFeatureSet(tc.proposedVersion, tc.supportedVersion)
err := types.VerifyProposedVersion(tc.proposedVersion, tc.supportedVersion, types.AllowNilFeatureSetMap)

require.Equal(t, tc.expPass, supported, "test case %d: %s", i, tc.name)
if tc.expPass {
require.NoError(t, err, "test case %d: %s", i, tc.name)
} else {
require.Error(t, err, "test case %d: %s", i, tc.name)
}
}

}
Expand Down
7 changes: 4 additions & 3 deletions x/ibc/04-channel/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc-transfer/types"
connectionutils "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/client/utils"
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
)
Expand Down Expand Up @@ -52,7 +53,7 @@ func NewChannelOpenInitCmd() *cobra.Command {
},
}
cmd.Flags().Bool(FlagOrdered, true, "Pass flag for opening ordered channels")
cmd.Flags().String(FlagIBCVersion, types.DefaultChannelVersion, "supported IBC version")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this maybe should just be an argument, since the default depends on what application you are creating a channel for, follow up pr since there is a todo for fixing counterparty version as well

cmd.Flags().String(FlagIBCVersion, ibctransfertypes.Version, "IBC application version")

return cmd
}
Expand Down Expand Up @@ -103,7 +104,7 @@ func NewChannelOpenTryCmd() *cobra.Command {
},
}
cmd.Flags().Bool(FlagOrdered, true, "Pass flag for opening ordered channels")
cmd.Flags().String(FlagIBCVersion, types.DefaultChannelVersion, "supported IBC version")
cmd.Flags().String(FlagIBCVersion, ibctransfertypes.Version, "IBC application version")

return cmd
}
Expand Down Expand Up @@ -147,7 +148,7 @@ func NewChannelOpenAckCmd() *cobra.Command {
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
cmd.Flags().String(FlagIBCVersion, types.DefaultChannelVersion, "supported IBC version")
cmd.Flags().String(FlagIBCVersion, ibctransfertypes.Version, "IBC application version")

return cmd
}
Expand Down
5 changes: 5 additions & 0 deletions x/ibc/04-channel/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ func (k Keeper) ChanOpenAck(
panic("cannot find connection")
}

// verify that chainB's proposed version identifier is supported by chainA
if err := connectiontypes.VerifyProposedVersion(channel.Version, counterpartyVersion, nil); err != nil {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

note: no support for disallowing nil feature sets. this is a little more tricky for channel version negotiation. I think it would require creating a channel keeper with the application version set inside a keeper field (perhaps a map). this seems like too much overhead for functionality that hasn't actually been requested yet

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I completely forgot there are ChanOpen callbacks. Applications can just throw an error on any version identifier/feature set they disagree with on ChanOpenAck.

return err
}

// counterparty of the counterparty channel end (i.e self)
expectedCounterparty := types.NewCounterparty(portID, channelID)
expectedChannel := types.NewChannel(
Expand Down
3 changes: 0 additions & 3 deletions x/ibc/04-channel/types/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ var (
_ exported.CounterpartyI = (*Counterparty)(nil)
)

// DefaultChannelVersion defines the default channel version used during handshake.
const DefaultChannelVersion = "1.0.0"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

there is no "channel version" only application versions for the associated channel.


// NewChannel creates a new Channel instance
func NewChannel(
state State, ordering Order, counterparty Counterparty,
Expand Down
24 changes: 12 additions & 12 deletions x/ibc/24-host/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import (
// - `[`, `]`, `<`, `>`
var IsValidID = regexp.MustCompile(`^[a-zA-Z0-9\.\_\+\-\#\[\]\<\>]+$`).MatchString

// IsValidConnectionVersion defines the regular expression to check if the
// string is in the form of a tuple consisting of a string identifier and
// a set of features. The entire version tuple must be enclosed in parentheses.
// The version identifier must not contain any commas. The set of features
// must be enclosed in brackets and separated by commas.
// IsValidnVersion defines the regular expression to check if the string is
// in the form of a tuple consisting of a string identifier and a set of
// features. The entire version tuple must be enclosed in parentheses. The
// version identifier must not contain any commas. The set of features must
// be enclosed in brackets and separated by commas.
//
// valid connection version = ([version_identifier], [feature_0, feature_1, etc])
var IsValidConnectionVersion = regexp.MustCompile(`^\([^,]+\,\[([^,]+(\,[^,]+)*)?\]\)$`).MatchString
// valid version = ([version_identifier], [feature_0, feature_1, etc])
var IsValidVersion = regexp.MustCompile(`^\([^,]+\,\[([^,]+(\,[^,]+)*)?\]\)$`).MatchString

// ICS 024 Identifier and Path Validation Implementation
//
Expand Down Expand Up @@ -83,15 +83,15 @@ func PortIdentifierValidator(id string) error {
return defaultIdentifierValidator(id, 2, 20)
}

// ConnectionVersionValidator is the default validator function for Connection
// versions. A valid version must be in semantic versioning form and contain
// only non-negative integers.
func ConnectionVersionValidator(version string) error {
// VersionValidator is the default validator function for IBC connection and
// channel versions. A valid version must follow the rules outlined in the
// `IsValidVersion` regex expression.
func VersionValidator(version string) error {
if strings.TrimSpace(version) == "" {
return sdkerrors.Wrap(ErrInvalidVersion, "version cannot be blank")
}

if !IsValidConnectionVersion(version) {
if !IsValidVersion(version) {
return sdkerrors.Wrapf(
ErrInvalidVersion,
"version '%s' must be in '(version_identifier,[feature_0, feature_1])' with no extra spacing", version,
Expand Down
2 changes: 1 addition & 1 deletion x/ibc/24-host/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func TestConnectionVersionValidator(t *testing.T) {

for _, tc := range testCases {

err := ConnectionVersionValidator(tc.id)
err := VersionValidator(tc.id)

if tc.expPass {
require.NoError(t, err, tc.msg)
Expand Down
3 changes: 2 additions & 1 deletion x/ibc/testing/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc-transfer/types"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
Expand All @@ -38,7 +39,7 @@ const (
UnbondingPeriod time.Duration = time.Hour * 24 * 7 * 3
MaxClockDrift time.Duration = time.Second * 10

ChannelVersion = "ics20-1"
ChannelVersion = ibctransfertypes.Version
InvalidID = "IDisInvalid"

ConnectionIDPrefix = "connectionid"
Expand Down