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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

NOTES.md

11.07 Kb · 162 lines
  1# Notes against the audit — realms/garden
  2
  3Things the audit surfaced that are product decisions, not engineering ones. Written here
  4per the owner's own rule (NOTES.md at repo root): checked facts and open trade-offs that
  5the pack/audit doesn't resolve go in a NOTES.md, not silently into code.
  6
  7## Open question: a realm can permanently own a gnome on behalf of no one (2026-10-09)
  8
  9**Status: unresolved. Needs the owner's decision before this realm goes near a chain.**
 10
 11`Adopt` derives the caller as `cur.Previous().Address()` (`garden.gno:230`, doc at
 12`garden.gno:188-195`) — "the account that crossed into this realm", matching
 13`examples/gno.land/r/demo/foo721`'s own idiom for caller identity. That address is not
 14restricted to an externally-owned account (a human holding a key). **Any realm** can call
 15`Adopt` the same way a human would, and `cur.Previous().Address()` then resolves to that
 16calling realm's own address, not to any human behind it.
 17
 18Combine that with two things this realm does deliberately, by design, per the brief's
 19"what it needs, and nothing more":
 20
 21- **No transfer.** `soulboundExtension.OnTransfer` panics unconditionally (rule 6, "every
 22  transfer fails"). This is load-bearing and correct — it's the whole soulbound guarantee
 23  the brief asks for.
 24- **No burn, no admin, no rename.** There is no path, for anyone, to undo an `Adopt` once
 25  it lands.
 26
 27Put together: if a realm (not a human) ever calls `Adopt` — whether on purpose, by a bug
 28in that realm, or because some future UI flow proxies a user's call through an
 29intermediate realm without meaning to — **that realm's own address becomes the permanent,
 30unrecoverable owner of a gnome.** No human can ever claim it. No key held by any person
 31unlocks it. It is not stuck in a recoverable sense (the audit found no reentrancy, no
 32capability leak, no exposed transfer that would make it *worse*); it is correctly,
 33permanently inaccessible to the person who might have expected to own it. The audit
 34flagged this as **unfixable within this realm's current design** — there is no check this
 35code can add that distinguishes "a human's wallet called this" from "a realm called this
 36and the human never sees the result" without also restricting legitimate future
 37composition (another realm adopting *on behalf of* a verified human, a batching/sponsor
 38flow, etc.).
 39
 40### The trade-off, stated plainly
 41
 42- **Restrict `Adopt` to human (EOA) callers only** — e.g. require some check along the
 43  lines of "this frame is a plain `maketx call`, not a realm-to-realm crossing call"
 44  before minting. `cur.Previous().IsUserCall()` is the name used for this in
 45  `pack/01-build-brief/gnogotchi-build-brief.md` and `pack/08-research/01-gno-land-platform.md`,
 46  but this engineering pass has not independently verified, against this repo's actual
 47  Gno toolchain, that a method of that name exists on `realm`/`cur.Previous()` — it is
 48  recorded here as the pack's own suggested name, not as a confirmed API this realm could
 49  call today. Verify it against the real SDK/runtime before relying on it. This closes the
 50  custody trap
 51  completely: a gnome can only ever be minted directly into a human wallet. The cost is
 52  that it forecloses, permanently and by construction, any future flow where a realm
 53  calls `Adopt` on a human's behalf — a sponsor/gift flow, a batching contract, a future
 54  wrapper realm the owner might want to build in a later phase. Re-opening that door later
 55  would need a new, deliberately-designed path (e.g. an explicit "adopt for" argument with
 56  its own trust model), not just deleting the restriction, because by then real gnomes may
 57  already exist that were minted the old way.
 58- **Leave it open, as today.** Preserves maximum flexibility for whatever Phase 2+ wants
 59  to build on top of `Adopt`. The cost is that the custody trap stays live for as long as
 60  this code ships as-is: the first realm that calls `Adopt` — malicious, buggy, or just
 61  built by someone who didn't read this note — mints a gnome nobody can ever claim, and
 62  there is no admin, burn or migration path in this realm to undo it after the fact.
 63
 64This is not a question the engineering brief answers, and it is not safe to guess: closing
 65it is a one-way, backward-compatible-looking change that quietly forecloses a product
 66surface; leaving it open is a one-way, invisible-until-it-happens risk. **The owner should
 67decide before this realm is deployed anywhere real funds or real users can reach it.**
 68
 69### Same open question, a second angle: a sponsor realm and the `OriginSend` guard (2026-10-09)
 70
 71`Adopt`'s `unsafe.OriginSend().IsZero()` check (`garden.gno`, inside `Adopt`) refuses the
 72call if the transaction's *origin* attached any coins at all. Re-checked this round: that
 73check is origin-scoped, not frame-scoped, so it inspects the whole transaction's send
 74regardless of how many realms it crossed through to reach `Adopt`. A "restrict to human
 75callers" fix for the custody question above does not make this interaction go away, and
 76may make it worse in one specific shape worth recording here rather than only in code:
 77
 78A future **sponsor realm** — one that takes a small fee for itself and then crosses into
 79`Adopt` on the human's behalf, remainder or nothing attached — would still be rejected by
 80today's `OriginSend` check, because that check sees the *origin's* original send, not what
 81actually reaches `Adopt`'s own frame. Nothing would be stranded in that flow (the sponsor
 82realm's own fee logic handles its own coins; `Adopt` itself receives nothing), but the
 83guard cannot tell "nothing reaches me" from "nothing reaches me because the origin sent
 84zero in the first place" apart from "something was sent upstream and I can't prove it
 85didn't end up here." It would reject a perfectly safe call.
 86
 87This is the same unresolved shape as the custody question above, from the other
 88direction: both are consequences of `Adopt` being reachable by something other than a
 89direct human `maketx call`, and both need the same owner decision — should `Adopt` ever be
 90callable by, or through, another realm at all, and if so, under what trust model — before
 91either the custody restriction or a sponsor-aware coin check is designed. Do not special-
 92case the `OriginSend` guard for "known sponsor realms" ad hoc; it would be solving half of
 93the same problem the custody note above already asks the owner to resolve in full.
 94
 95## Open question: the name allowlist excludes every combining mark, so whole writing systems are unnameable (2026-10-09, third audit round, finding Y7)
 96
 97**Status: this is a defensible, already-shipped trade-off, not a bug. Recorded here because
 98which writing systems the game supports is a product decision, not an engineering one —
 99same rule as the custody question above.**
100
101`validNameRune` (`garden.gno`) admits only individual Unicode letters (`unicode.IsLetter`),
102digits (`unicode.IsDigit`), space, hyphen and apostrophe — each check applied one rune at a
103time, with no notion of a rune combining with the one before or after it. Unicode's
104combining-mark categories (chiefly Mn, "nonspacing mark") are entirely outside that
105allowlist: no combining mark of any kind can ever pass `validNameRune`, today or after any
106future Unicode version, since the exclusion is by general category, not a specific list
107that could go stale.
108
109**What this actually forecloses**, stated plainly because "a name allowlist" undersells it:
110a large number of ordinary, everyday spellings are only representable with at least one
111base letter plus one or more combining marks, not as a sequence of precomposed
112letter-only code points. Concretely, and non-exhaustively:
113
114- **NFD-normalised Latin.** Some input methods and some Apple keyboards produce "é" as
115  the two-code-point sequence `e` + U+0301 COMBINING ACUTE ACCENT (NFD) rather than the
116  single precomposed U+00E9 (NFC). A name typed or pasted in NFD form — "José" being the
117  canonical example — is rejected today even though the visually-identical NFC spelling of
118  the same name is accepted.
119- **Devanagari, as ordinarily written.** Vowel signs (mātrā) are combining marks; ordinary
120  Devanagari text is not just base consonants.
121- **Thai, as ordinarily written.** Vowel and tone marks are combining marks positioned
122  above/below the preceding consonant.
123- **Hebrew with niqqud** (vowel points) and **Arabic with harakat** (vowel diacritics) —
124  both are combining marks layered on consonant letters; the unvocalised consonant-only
125  spelling of a name may pass where the fully-vocalised, more traditional spelling would
126  not.
127
128None of this is specific to "foreign" scripts as a category — `TestAdoptAcceptsCrossScriptHomoglyphs`
129already proves single-code-point non-Latin letters (Cyrillic, Greek, fullwidth, etc.) are
130accepted just fine. The exclusion is specifically and only about *combining* marks, which
131happen to be load-bearing for several scripts' ordinary orthography and merely decorative
132for Latin's.
133
134**The benefit this trade buys, stated equally plainly:** combining marks are exactly the
135mechanism "zalgo text" (stacking dozens of combining marks on a single base letter to
136produce the glitchy, vertically-exploding look) depends on. Excluding every combining mark
137makes zalgo-stacked names structurally impossible in this realm, not merely discouraged —
138there is no finite deny-list to maintain and no risk of a renderer-breaking name getting
139through because some zalgo-capable combining mark wasn't on it. This is a real, concrete
140security/quality property, not a hypothetical one: Render already embeds the name directly
141into a markdown heading with no further escaping (see `validNameRune`'s own comment), and a
142sufficiently mark-stacked name is exactly the kind of input that has broken renderers and
143UI layouts elsewhere on the web.
144
145**Why shipping strict is the safe order, not just the easy one:** relaxing this later
146(admitting combining marks, most plausibly gated to "must immediately follow a base
147letter/digit" so a name still can't *start* with a stray mark or stack unboundedly) is
148backward-compatible — every name accepted today stays accepted, and the newly-expanded set
149of acceptable names doesn't retroactively break anything already minted. Tightening a
150currently-open rule later is not backward-compatible in the same way: with no rename, no
151burn and no admin anywhere in this realm, any name already minted under a looser rule is on
152chain forever regardless of what a later, stricter rule would say about it — so a mistake
153in the direction of "too permissive" cannot be undone, while a mistake in the direction of
154"too strict" (this realm's current choice) costs only a future relaxation, not a migration.
155
156**The decision this note asks the owner to make:** whether Phase 1+ should relax
157`validNameRune` to admit combining marks (and if so, under what shape of gate) so players
158whose names are ordinarily written with them can spell them as intended, accepting the
159reintroduced zalgo-stacking risk and designing a mitigation for it (e.g. a cap on
160consecutive combining marks per base character) — or whether excluding them permanently,
161as today, is the intended answer for this game. This is not a question the engineering
162brief or the audit resolves, and it is not safe to guess which way the owner wants it.