Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}