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}