Conversation
cross1 was a temporary uverse sentinel easing the bare-cross -> cross(rlm) migration. The migration is complete and per policy (interrealm_v2.md) nothing should be using it. Remove the uverse def, gno0p9 typechecker shim line, preprocessor recognizer/lowering cases, and the transpiler ident rewrite; the compiler-synthesized .origin lowering path is unchanged. The legacy filetest becomes zrealm_cross1_removed.gno, an error test asserting the name no longer resolves at both typecheck and preprocess. migration_guide.md par.16 is rewritten as the direct bare-cross -> cross(rlm) recipe; interrealm_v2.md marks its cross1 steps historical. ADR: gnovm/adr/prxxxx_remove_cross1.md.
A host realm can mint a sub-realm token for an internal actor
(cur.Sub("dao/42")): a realm value whose PkgPath is the synthesized
"host:subpath" (':' is reserved; explicitly rejected at
ValidateMemPackageAny) and whose Address derives from that synthesized
path. Downstream callees see the sub-identity through the ordinary
cur.Previous() idiom after crossing with the token; the host is
recoverable via chain.SplitPkgSubPath.
Mechanics:
- .grealm gains subpath/parent fields (transient, never persisted;
legacy 3-field values read through zero-defaulting accessors). The
isOriginRealmHIV persistence carve-out excludes sub-tokens.
- Sub() native: gas charged up front (mirroring chain.packageAddress
calibration; uverse natives bypass the native gas table), subpath
rules + derivation asserts, strict pointer-identity entry guard
against the topmost crossing frame's Cur (sub-of-sub structurally
blocked; deliberately NOT the relaxed IsCurrent path), ephemeral
(/e/) hosts forbidden, caller-pkgpath check (no foreign minting).
- realmIsCurrentOnMachine accepts sub-tokens via the internal parent
reference, so IsCurrent()/cross() work while the minting cur is
live; classification predicates return the safe values naturally
(IsCode true; IsUser/IsUserCall/IsUserRun/IsEphemeral false).
- unsafe.{Current,Previous}Realm parity: execctx.GetRealm (and the
test-stdlib mirror) now walk the presented-identity chain (crossing
frame's Cur -> prev), so unsafe.* agrees with cur.Previous() for
sub-tokens; legacy stage fallbacks unchanged at the chain root.
- gno0p9 typechecker shim extended in place with Sub (additive on the
sealed realm interface; consensus-visible, ships with the same
coordinated upgrade as the cross1 removal).
- chain.DerivePkgSubAddr/SplitPkgSubPath stdlib helpers (pure gno).
- banker.NewBanker forbids RealmIssue/OriginSend for sub-tokens;
RealmSend works unchanged (rlm-as-capability, no native changes).
Tests: filetests for minting/classification, parity (heights 0/1),
derivation equivalence, sub-of-sub and staleness rejection, subpath
validation, persistence refusal, banker flows; keeper test for the
/e/ MsgRun ban; unit tests for the ':' reservation and legacy 3-field
shape guards. Golden updates: 5-field .grealm rendering
(zrealm_panic), allocation size (alloc_7a), earlier typecheck error
for non-realm implementors (zrealm_seal_realm), and the pinned
crossrealm38 multistore hash (realm interface TypeID shift — the
intended consensus change).
ADR: gnovm/adr/prxxxx_realm_sub.md (full design spec).
Note: cross(...) requires a bare realm-typed identifier, so the idiom
is two-step: sub := cur.Sub(x); f(cross(sub), ...).
The realm interface TypeID and gno0p9 typecheck shim grew with the Sub method, shifting gas on realm-loading paths (~0.0001% on gc's alloc scenario; ~3% headroom bumps on two gas-wanted values). Pure gas-number updates; no test logic changed.
Quality pass over the sub-realm implementation (no behavior change to the feature surface): - Lazy field mint: primary curs mint the 3-field .grealm shape again; only Sub() builds the 5-field form. Readers of fields >=3 already go through realmSubpathOf/realmParentOf (zero-defaulting), so this is transparent — and it restores primary allocation to exactly the pre-feature cost. Reverts the alloc_7a/zrealm_panic goldens and two of the three txtar gas-golden changes back to master; the only remaining consensus-state deltas are the intended realm-interface TypeID apphash re-pin and one gas-wanted bump (the banker sub-token guard's pkgpath check on the OriginSend path). - Dedup the crossing-frame walk: one Machine.topCrossingCur backs callingCurOrOrigin, topmostCrossingFrameCurHIV, and the new exported PresentedRealmAt (was heading toward four hand-synced copies). - GetRealm / X_getRealm call PresentedRealmAt instead of duplicating the identity-chain loop; the prod legacy walk shrinks to the boundary switch and fails loudly rather than serving context-chain answers if the chain-length invariant ever breaks. - Test mirror gates on live overrides (TestOverridden) instead of map length, so a popped helper's SetRealm no longer permanently disables the identity chain. - Banker guard gates on bt first (int compare) and collapses to one fail-closed panic via bt.String(). - Sub native: drop the always-false nil-sv guard; hoist synthesized so the run-path assert (dead while the regex stays colon-free; kept as defense-in-depth) doesn't re-concat. - Tests: zrealm_sub_stale now asserts the recoverable Sub-on-stale path + live cross (was unverified under a lone Error directive); zrealm_sub_stale_cross covers the fatal cross; native_gas_test guards OpCPUSubRealm* against packageAddress recalibration drift; drop cargo-culted _ int in the banker test; correct the nested-test comment about which guard fires.
- zrealm_sub_foreign.gno: the caller-pkgpath guard's foreign-minting rejection (borrow rule #1: crossrealm_b runs with m.Realm = itself, holding the caller's live cur — Sub refuses; surfaces as an abort). Adds the TrySubOn fixture to r/tests/vm/crossrealm_b. This was the one Sub() guard with no coverage. - gno-interrealm-v2.md gains §5.5 (sub-realm identities: semantics, guards, opt-in matching idiom, tooling helpers, trust note). - CONSTITUTION.md: one-line realization note on the DAO-address clause, pointing at §5.5.
… total cap, Subpath()
Pre-freeze hardening of the realm.Sub surface (the realm interface
enters the consensus-frozen gno0p9 shim; these are cheap now,
expensive-or-impossible later):
- Subpath grammar frozen strict: segment ("/" segment)*, segment =
[a-z0-9]([a-z0-9_.-]*[a-z0-9])?. Excludes uppercase, whitespace,
control bytes, non-ASCII (no NFC/NFD or RTL address ambiguity), ':',
NUL, empty segments, edge punctuation, '..'/'.' traversal. Loosening
later is additive; tightening later would strand funds at addresses
derived from now-illegal subpaths, so the narrow set is chosen up
front.
- Length cap moved to the *total* synthesized "host:subpath" (<= 256,
the pkgpath limit), not the subpath alone — keeps downstream
pkgpath-sized buffers valid instead of admitting ~513-byte PkgPath().
- Add realm.Subpath() ("" for primaries): the canonical "am I a sub,
of what" accessor, derived from PkgPath() so it agrees with
chain.SplitPkgSubPath for any value. First consumer is the banker
ban. Deliberately NOT Host() realm (would let a callee holding
cur.Previous() reconstruct a host-shaped value and bank over the
host treasury).
- chain.assertValidSubpath mirrors the Go grammar byte-for-byte so
DerivePkgSubAddr can never derive an address Sub would refuse.
Tests: TestIsValidSubpath (pinned accept/reject sets),
TestSubRealmPathErrorTotalCap, expanded zrealm_sub_validation filetest,
Subpath() assertions. Apphash re-pinned for the interface TypeID
growth. Docs: ADR + gno-interrealm-v2 §5.5 updated.
Pre-existing base-v2 authorization gap (independent of sub-realms,
surfaced while reviewing the Sub banker-compat claim): NewBanker had no
IsCurrent() check for RealmSend/RealmIssue, and the only spend gate is
SendCoins's pkgAddr==from. Because cur.Previous() hands every callee a
realm value bearing its CALLER's address (IsCurrent()==false), a callee
could build NewBanker(RealmSend, cur.Previous()) (pkgAddr=caller) and
SendCoins(caller, attacker, coins) — the gate passes and the bank
keeper moves the caller's coins. For a MsgCall that means any realm
could drain the user who called it; for realm-to-realm, a callee drains
its caller. Unbounded, no privileged position, no -send required;
confirmed by an end-to-end drain against the real bank keeper.
docs/resources/gno-security.md Class 2 ('never trust rlm.Address()
without IsCurrent()') already prescribes the fix; NewBanker was an
unfixed instance, and banker_security.txtar missed it (it only built
bankers from the realm's own cur, never from cur.Previous()).
Fix: require rlm.IsCurrent() at NewBanker construction (covers
OriginSend/RealmSend/RealmIssue; Readonly panics earlier). Construction
-time is correct — the banker value holds no realm handle, so use-time
re-check is impossible, and the legitimate pattern is
construct-from-live-cur-then-store/reuse (treasury wrappers,
banker_persistence). Live sub-tokens pass (IsCurrent is parent-anchored),
so the sub-token treasury flow is preserved.
Tests: banker_security.txtar TEST 6 (real-keeper drain via cur.Previous()
blocked; victim not drained), zrealm_banker_previous.gno (VM-level
rejection). crossrealm21/22/28 goldens shift (crossrealm_b gained the
TryBankerOnPrevious fixture — object-id/line only); apphash re-pinned
(stdlib source change). ADR banker section rewritten to document the
prerequisite fix rather than assuming the path sound.
…ve rejection Close the coverage gaps the review flagged: - zrealm_sub_foreign_present.gno + subhost fixture: sub-identity presented across a real package boundary — callee reads cur.Previous().PkgPath()/Address()/Subpath()/IsUserCall() through the cross (prior tests only crossed within one package or tested rejection). - subrealm_run_parity.txtar: unsafe.* <-> cur.Previous() parity at MsgRun entry (StageRun, /e/ origin) — the counterpart to zrealm_sub1's MsgCall-entry parity the ADR requires for both. - zrealm_sub_derive_reject.gno: chain.DerivePkgSubAddr REJECTS the same grammar cur.Sub does (+ colon-host derivation-safety assert), pinning the drift-prevention claim on the failure side (sub2 only covered the accepting path). Positive control: a run-shaped subpath hashes normally, never address-extracted. Docs: ADR banker snippet updated to the shipped fail-closed 'bt != RealmSend' form (was a stale switch); decisions list corrected to 'only RealmSend permitted for sub-tokens'. Deduped TestIsValidSubpath invalid set.
Collaborator
🛠 PR Checks SummaryAll Automated Checks passed. ✅ Manual Checks (for Reviewers):
Read More🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers. ✅ Automated Checks (for Contributors):No automated checks match this pull request. ☑️ Contributor Actions:
☑️ Reviewer Actions:
📚 Resources:Debug
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds cur.Sub(subpath): a realm can mint a sub-realm identity token for an
internal actor (e.g. a DAO in a registry) — a realm value with synthesized
pkgpath "host:subpath" and a derived address. Downstream callees see it via
the normal cur.Previous() idiom after crossing; ":" is reserved so exact-match
pkgpath auth is never silently broadened. Includes Subpath()/DerivePkgSubAddr/
SplitPkgSubPath, a frozen-strict subpath grammar, and unsafe.* parity.
Security fix (independent, pre-existing base-v2 hole): NewBanker now requires
rlm.IsCurrent(). Without it a callee could build a RealmSend banker from
cur.Previous() and drain its caller (any realm draining any user who calls it).
Confirmed end-to-end against the real bank keeper; regression-tested.
Also removes the legacy cross1 migration sentinel.
Consensus-breaking (realm interface grows Sub/Subpath; gno0p9 extended in
place) — requires a coordinated node upgrade. Any network retaining state
should be swept for cross1 first.
Areas: GnoVM (uverse, preprocess, transpiler, gotypecheck), stdlib
(chain/banker, chain/address, execctx), tests (filetests, txtars, unit,
keeper), docs (interrealm-v2 §5.5, CONSTITUTION), ADRs. AI-assisted.