Search Apps Documentation Source Content File Folder Download Copy Actions Download

users.gno

3.02 Kb · 85 lines
 1package namereg
 2
 3import (
 4	"chain"
 5	"chain/runtime/unsafe"
 6
 7	"gno.land/p/moul/fifo"
 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}