client.gno
2.80 Kb · 81 lines
1package types
2
3import (
4 "math"
5 "regexp"
6 "strconv"
7 "strings"
8
9 "gno.land/p/aib/ibc/host"
10 "gno.land/p/nt/ufmt/v0"
11)
12
13// ValidateClientType validates the client type. It cannot be blank or empty.
14// It must be a valid client identifier when used with '0' or the maximum
15// uint64 as the sequence.
16func ValidateClientType(clientType string) error {
17 if strings.TrimSpace(clientType) == "" {
18 return ufmt.Errorf("client type cannot be blank")
19 }
20
21 smallestPossibleClientID := FormatClientIdentifier(clientType, 0)
22 largestPossibleClientID := FormatClientIdentifier(clientType, uint64(math.MaxUint64))
23
24 // IsValidClientID will check client type format and if the sequence is a uint64
25 if !IsValidClientID(smallestPossibleClientID) {
26 return ufmt.Errorf("not a valid clientID: %s", smallestPossibleClientID)
27 }
28
29 if err := host.ClientIdentifierValidator(smallestPossibleClientID); err != nil {
30 return err
31 }
32 if err := host.ClientIdentifierValidator(largestPossibleClientID); err != nil {
33 return err
34 }
35
36 return nil
37}
38
39// FormatClientIdentifier returns the client identifier with the sequence appended.
40// This is an SDK specific format not enforced by IBC protocol.
41func FormatClientIdentifier(clientType string, sequence uint64) string {
42 return ufmt.Sprintf("%s-%d", clientType, sequence)
43}
44
45// IsClientIDFormat checks if a clientID is in the format required on the SDK
46// for parsing client identifiers. The client identifier must be in the form:
47// `{client-type}-{N}` which per the specification only permits ASCII for the
48// {client-type} segment and 1 to 20 digits for the {N} segment.
49// `([\w-]+\w)?` allows for a letter or hyphen, with the {client-type} starting
50// with a letter and ending with a letter, i.e.
51// `letter+(letter|hyphen+letter)?`.
52var IsClientIDFormat = regexp.MustCompile(`^\w+([\w-]+\w)?-[0-9]{1,20}$`).MatchString
53
54// IsValidClientID checks if the clientID is valid and can be parsed into the client
55// identifier format.
56func IsValidClientID(clientID string) bool {
57 _, _, err := ParseClientIdentifier(clientID)
58 return err == nil
59}
60
61// ParseClientIdentifier parses the client type and sequence from the client identifier.
62func ParseClientIdentifier(clientID string) (string, uint64, error) {
63 if !IsClientIDFormat(clientID) {
64 return "", 0, ufmt.Errorf("invalid client identifier %s is not in format: `{client-type}-{N}`", clientID)
65 }
66
67 splitStr := strings.Split(clientID, "-")
68 lastIndex := len(splitStr) - 1
69
70 clientType := strings.Join(splitStr[:lastIndex], "-")
71 if strings.TrimSpace(clientType) == "" {
72 return "", 0, ufmt.Errorf("client identifier must be in format: `{client-type}-{N}` and client type cannot be blank")
73 }
74
75 sequence, err := strconv.ParseUint(splitStr[lastIndex], 10, 64)
76 if err != nil {
77 return "", 0, ufmt.Errorf("failed to parse client identifier sequence")
78 }
79
80 return clientType, sequence, nil
81}