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