Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

api.gno

3.36 Kb · 96 lines
 1package namereg
 2
 3import (
 4	"errors"
 5	"regexp"
 6	"strings"
 7)
 8
 9// Open Nym Tier username format. Anchored.
10//   - literal `nym-` prefix (4 chars)
11//   - 5-13 lowercase ASCII letters (the alpha stem)
12//   - exactly 3 trailing decimal digits
13//
14// Total length 12-20 chars. Distinct by length from `g1...` addresses
15// which are always 40 chars.
16const reNymFormat = `^nym-[a-z]{5,13}\d{3}$`
17
18var reNym = regexp.MustCompile(reNymFormat)
19
20// Reserved alpha-stem prefixes. Names whose stem starts with one of
21// these are rejected at format-validation time with ErrReservedPrefix
22// (clearer than ErrCanonicalCollision after the fact).
23//
24// `gi` is intentionally NOT listed: in most rendering targets `i` is
25// visually distinct enough from `1`/`l` that legitimate `gi*` names
26// (giggles, gimbal, gift, etc.) should remain registerable. Phishing
27// protection for the visual class is still enforced by canonical-
28// collision detection in r/sys/users — once any `gi*` or `gl*` name
29// is registered, all variants under the {l,i,1}→i canonicalization
30// collide.
31//
32// `gl` and `g1` remain listed because they're more visually
33// confusable with the bech32 address prefix `g1`. `g1` itself is
34// unreachable through the alpha-only stem regex; defense-in-depth
35// for any future regex relaxation.
36var reservedPrefixes = []string{"gl", "g1", "gno", "atom", "atone", "photon", "cosmos"}
37
38// Exported error sentinels returned by ValidateNymFormat. Use
39// errors.Is or direct equality; do not string-match.
40//
41// ErrReservedPrefix's message is built from reservedPrefixes at package
42// init time so the surfaced list never drifts from the actual policy.
43//
44// Canonical-collision detection moved to r/sys/users in Option B.
45// Consumers that previously caught namereg.ErrCanonicalCollision
46// should switch to susers.ErrCanonicalCollision.
47var (
48	ErrInvalidFormat  = errors.New("namereg: name must match nym-[a-z]{5,13}\\d{3}")
49	ErrReservedPrefix = errors.New("namereg: stem starts with a reserved prefix (" + strings.Join(reservedPrefixes, "/") + ")")
50	ErrBlacklisted    = errors.New("namereg: stem matches a reserved role name")
51)
52
53// IsReserved reports whether the given alpha stem matches a reserved
54// role name (with implicit `s`-suffix expansion). The check is
55// canonicalized — so `vital1k`-style l-substituted variants of a
56// reserved name are also caught. O(1) backed by `reservedSet` built
57// in init().
58func IsReserved(stem string) bool {
59	_, found := reservedSet[Canonicalize(stem)]
60	return found
61}
62
63// ValidateNymFormat checks the regex, prefix-exclusion, and reserved-
64// name rules in that order. Returns one of the exported sentinel
65// errors per failure mode, or nil on success.
66//
67// Does NOT run the canonical-collision check — that lives in r/sys/users
68// (susers.IsCanonicalTaken or, atomically with the write, inside
69// susers.RegisterUser).
70func ValidateNymFormat(username string) error {
71	if !reNym.MatchString(username) {
72		return ErrInvalidFormat
73	}
74
75	// Stem is everything between `nym-` (4 chars) and the trailing
76	// 3 digits. Regex guarantees 5..13 alpha chars in this slice.
77	stem := username[4 : len(username)-3]
78
79	for _, p := range reservedPrefixes {
80		if strings.HasPrefix(stem, p) {
81			return ErrReservedPrefix
82		}
83	}
84
85	if IsReserved(stem) {
86		return ErrBlacklisted
87	}
88
89	return nil
90}
91
92// IsPaused exposes the realm's pause flag for cross-controller
93// coordination.
94func IsPaused() bool {
95	return paused
96}