diff --git a/CHANGELOG.md b/CHANGELOG.md index 09179f2640..34c617d89f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,10 @@ and provided directly the IAVL store. * (types) [\#5533](https://github.com/cosmos/cosmos-sdk/pull/5533) Refactored `AppModuleBasic` and `AppModuleGenesis` to now accept a `codec.JSONMarshaler` for modular serialization of genesis state. +### Features + +* (x/ibc) [\#5588](https://github.com/cosmos/cosmos-sdk/pull/5588) Add [ICS 024 - Host State Machine Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements) subpackage to `x/ibc` module. + ### Bug Fixes * (client) [\#5618](https://github.com/cosmos/cosmos-sdk/pull/5618) Fix crash on the client when the verifier is not set. diff --git a/types/router.go b/types/router.go index f3593f5530..12d0455f80 100644 --- a/types/router.go +++ b/types/router.go @@ -2,9 +2,27 @@ package types import "regexp" -// IsAlphaNumeric defines a regular expression for matching against alpha-numeric -// values. -var IsAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString +var ( + // IsAlphaNumeric defines a regular expression for matching against alpha-numeric + // values. + IsAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString + + // IsAlphaLower defines regular expression to check if the string has lowercase + // alphabetic characters only. + IsAlphaLower = regexp.MustCompile(`^[a-z]+$`).MatchString + + // IsAlphaUpper defines regular expression to check if the string has uppercase + // alphabetic characters only. + IsAlphaUpper = regexp.MustCompile(`^[A-Z]+$`).MatchString + + // IsAlpha defines regular expression to check if the string has alphabetic + // characters only. + IsAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString + + // IsNumeric defines regular expression to check if the string has numeric + // characters only. + IsNumeric = regexp.MustCompile(`^[0-9]+$`).MatchString +) // Router provides handlers for each transaction type. type Router interface { diff --git a/x/ibc/24-host/errors.go b/x/ibc/24-host/errors.go new file mode 100644 index 0000000000..26b7919e6c --- /dev/null +++ b/x/ibc/24-host/errors.go @@ -0,0 +1,15 @@ +package host + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// SubModuleName defines the ICS 24 host +const SubModuleName = "host" + +// IBC client sentinel errors +var ( + ErrInvalidID = sdkerrors.Register(SubModuleName, 1, "invalid identifier") + ErrInvalidPath = sdkerrors.Register(SubModuleName, 2, "invalid path") + ErrInvalidPacket = sdkerrors.Register(SubModuleName, 3, "invalid packet") +) diff --git a/x/ibc/24-host/utils.go b/x/ibc/24-host/utils.go new file mode 100644 index 0000000000..c75f356561 --- /dev/null +++ b/x/ibc/24-host/utils.go @@ -0,0 +1,11 @@ +package host + +// RemovePath is an util function to remove a path from a set. +func RemovePath(paths []string, path string) ([]string, bool) { + for i, p := range paths { + if p == path { + return append(paths[:i], paths[i+1:]...), true + } + } + return paths, false +} diff --git a/x/ibc/24-host/validate.go b/x/ibc/24-host/validate.go new file mode 100644 index 0000000000..e75e63e6c2 --- /dev/null +++ b/x/ibc/24-host/validate.go @@ -0,0 +1,96 @@ +package host + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// ICS 024 Identifier and Path Validation Implementation +// +// This file defines ValidateFn to validate identifier and path strings +// The spec for ICS 024 can be located here: +// https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements + +// ValidateFn function type to validate path and identifier bytestrings +type ValidateFn func(string) error + +func defaultIdentifierValidator(id string, min, max int) error { + // valid id MUST NOT contain "/" separator + if strings.Contains(id, "/") { + return sdkerrors.Wrapf(ErrInvalidID, "identifier %s cannot contain separator '/'", id) + } + // valid id must be between 10 and 20 characters + if len(id) < min || len(id) > max { + return sdkerrors.Wrapf(ErrInvalidID, "identifier %s has invalid length: %d, must be between %d-%d characters", id, len(id), min, max) + } + // valid id must contain only lower alphabetic characters + if !sdk.IsAlphaLower(id) { + return sdkerrors.Wrapf(ErrInvalidID, "identifier %s must contain only lowercase alphabetic characters", id) + } + return nil +} + +// DefaultClientIdentifierValidator is the default validator function for Client identifiers +// A valid Identifier must be between 10-20 characters and only contain lowercase +// alphabetic characters, +func DefaultClientIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 10, 20) +} + +// DefaultConnectionIdentifierValidator is the default validator function for Connection identifiers +// A valid Identifier must be between 10-20 characters and only contain lowercase +// alphabetic characters, +func DefaultConnectionIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 10, 20) +} + +// DefaultChannelIdentifierValidator is the default validator function for Channel identifiers +// A valid Identifier must be between 10-20 characters and only contain lowercase +// alphabetic characters, +func DefaultChannelIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 10, 20) +} + +// DefaultPortIdentifierValidator is the default validator function for Port identifiers +// A valid Identifier must be between 2-20 characters and only contain lowercase +// alphabetic characters, +func DefaultPortIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 2, 20) +} + +// NewPathValidator takes in a Identifier Validator function and returns +// a Path Validator function which requires path only has valid identifiers +// alphanumeric character strings, and "/" separators +func NewPathValidator(idValidator ValidateFn) ValidateFn { + return func(path string) error { + pathArr := strings.Split(path, "/") + for _, p := range pathArr { + // Each path element must either be valid identifier or alphanumeric + err := idValidator(p) + if err != nil && !sdk.IsAlphaNumeric(p) { + return sdkerrors.Wrapf(ErrInvalidPath, "path %s contains invalid identifier or non-alphanumeric path element: %s", path, p) + } + } + return nil + } +} + +// DefaultPathValidator takes in path string and validates +// with default identifier rules. This is optimized by simply +// checking that all path elements are alphanumeric +func DefaultPathValidator(path string) error { + pathArr := strings.Split(path, "/") + if pathArr[0] == path { + return sdkerrors.Wrapf(ErrInvalidPath, "path %s doesn't contain any separator '/'", path) + } + + for _, p := range pathArr { + // Each path element must be alphanumeric and non-blank + if strings.TrimSpace(p) == "" || !sdk.IsAlphaNumeric(p) { + return sdkerrors.Wrapf(ErrInvalidPath, "path %s contains an invalid non-alphanumeric character: '%s'", path, p) + } + } + return nil +}