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

users.gno

3.03 Kb · 85 lines
 1package namereg
 2
 3import (
 4	"chain"
 5	"chain/runtime/unsafe"
 6
 7	"gno.land/p/moul/fifo/v0"
 8	susers "gno.land/r/sys/users"
 9)
10
11// MinRegisterPrice is the lowest price (in ugnot) that
12// ProposeNewRegisterPrice will accept. Set to 0 — registration is free
13// by default; governance can raise the price via ProposeNewRegisterPrice
14// without a floor.
15const MinRegisterPrice = int64(0)
16
17var (
18	registerPrice = int64(0)      // free by default; governance can raise via ProposeNewRegisterPrice
19	latestUsers   = fifo.New(100) // Save the latest 100 users for rendering purposes
20)
21
22// Register registers a new username for the caller.
23//
24// Valid usernames match `nym-[a-z]{5,13}\d{3}`:
25//   - literal `nym-` prefix (4 chars)
26//   - 5-13 lowercase letters (the alpha stem)
27//   - exactly 3 trailing decimal digits
28//
29// Total length 12-20 chars. The alpha stem additionally must NOT start
30// with `gno`/`gi`/`gl` and must not match a reserved role name (with
31// implicit `s`-suffix expansion). See ValidateNymFormat for the
32// format/blacklist check.
33//
34// Canonical-collision detection is enforced atomically by
35// susers.RegisterUser via the unified canonical store in r/sys/users
36// (decision: per Option B, every controller participates in the same
37// canonical-form lookup keyed by full canonical name).
38//
39// Only direct EOA (maketx call) invocations are supported.
40func Register(cur realm, username string) {
41	// Anti-squatting payment check, two paired guards:
42	//
43	//   (a) PreviousRealm must be a pure EOA (IsUserCall: pkgPath == "").
44	//       This excludes intermediate code realms AND user-run ephemeral
45	//       realms ("maketx run" scripts). Both can attach -send to the
46	//       tx but spend the coins on something other than forwarding to
47	//       this realm, leaving OriginSend() describing a phantom payment.
48	//       IsUserCall is the only PreviousRealm shape where the tx-send
49	//       envelope is guaranteed to have landed at this realm's address.
50	//
51	//   (b) OriginSend amount must exactly equal registerPrice. Verifies
52	//       the tx actually attached the expected amount.
53	//
54	// Both checks MUST run together. Removing (a) alone makes (b) meaningless
55	// because OriginSend() describes tx intent, not realm receipt.
56	if !cur.Previous().IsUserCall() {
57		panic(ErrNonUserCall)
58	}
59
60	if paused {
61		panic(ErrPaused)
62	}
63
64	if unsafe.OriginSend().AmountOf("ugnot") != registerPrice {
65		panic(errInvalidPayment())
66	}
67
68	// Format + prefix + reserved-name check. ValidateNymFormat returns
69	// one of ErrInvalidFormat, ErrReservedPrefix, ErrBlacklisted.
70	if err := ValidateNymFormat(username); err != nil {
71		panic(err)
72	}
73
74	// Delegate the canonical-collision check + nameStore write atomically
75	// to r/sys/users. Returns susers.ErrCanonicalCollision if the
76	// canonical form clashes with an existing registration in any
77	// controller.
78	registrant := cur.Previous().Address()
79	if err := susers.RegisterUser(cross(cur), username, registrant); err != nil {
80		panic(err)
81	}
82
83	latestUsers.Append(username)
84	chain.Emit("Registration", "address", registrant.String(), "name", username)
85}