Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}