Repository navigation
Conversation
…e foreign finalize
…ity, address maps, leave non-realm objects out
…for the complete direction Signed-off-by: moul <94029+moul@users.noreply.github.com>
…bject it owns Signed-off-by: moul <94029+moul@users.noreply.github.com>
…s never reissued Signed-off-by: moul <94029+moul@users.noreply.github.com>
… grc20 Signed-off-by: moul <94029+moul@users.noreply.github.com>
… sources moved Signed-off-by: moul <94029+moul@users.noreply.github.com>
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
|
…ce must declare one in go too The transpiler emits a direct Go call for a native binding and passes the argument with the type gno gave it, so the Go parameter has to be a type the gno one is assignable to. objectInfo declared gno.TypedValue against a gno `any`: it links, because a Go gno.TypedValue parameter is genstd's escape hatch from reflect-based conversion and matches any gno type, and it is correct under the interpreter, but it cannot be called from Go. `gno tool transpile -C examples --gobuild .` fails the moment any transpiled package reaches the file. The Go side now declares `any` as well, and genstd hands over the machine's gno.TypedValue boxed in it, still unconverted; the native asserts it back. That assertion fails only in the Go program the transpiler emits, which has no VM behind it, and the no-identity answer is true of every value outside the VM. The defect is inherited rather than new: reflect.go is byte-identical to #6214, whose gno2go job is green only because nothing there imports chain/reflect in gno, so the file is never transpiled. chain/banker imports it on this branch. genstd also documents the gno.TypedValue-as-parameter hazard on isTypedValue, and rejects an empty-interface result with a message naming the alternative rather than generating code that cannot compile. No public API changes: reflect.gno is untouched. Signed-off-by: moul <94029+moul@users.noreply.github.com>
This branch has not been deployed
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.
This is #6214 finished rather than #6214 restated. It keeps that PR's API and
its five commits unchanged as its base, and answers the four questions it left
open: who may spend an object's money, what the address means once the
object is gone, what admits a new kind of value to the addressable set,
and why #6026's grc20 fix is still not here.
Morgan asked for "either a simpler solution or a more complete idea of what it
means to give addresses to objects". This is the second one.
Closes nothing on its own; it is the base the #6026 fix is written against. See
gnovm/adr/chain_reflect_object_identity.mdfor the full record.What #6214 says, and what this adds
An address that can receive and cannot release is an address value goes into
and does not come out of. That is the sentence this branch deletes.
1. Spending — implemented
The authority rule: an object address is spendable by the realm whose storage
clock minted the object's identity, and by nothing else.
It is not one policy among several. An
ObjectIDis{PkgID, NewTime}andPkgIDisPkgIDFromPkgPath(<realm path>), so the owner is inside theidentity rather than recorded beside it, and "is this mine" is
oid.PkgID == PkgIDFromPkgPath(rlm.PkgPath()): an equality between two valuesthe VM computed. Every alternative — an owner field, a transferable capability,
a registry of delegates — is realm state, which a realm can get wrong and which
an address created before it and outliving it would then contradict.
The banker sends and does nothing else.
IssueCoin/RemoveCoinpanic (a denomis namespaced by a realm path; an object has none). There is no origin-send
variant (the envelope is credited to the realm's address, never an object's). A
sub-realm token is refused (its PkgID is not the host's).
Nothing on the Gno side is trusted.
NewObjectBankerreads one thing out of thereflect.Objectit is handed —obj.ID(), a string — and the native re-parsesit into an
ObjectID, re-renders and compares it (so a short hashlet thathex.Decodewould zero-extend is rejected), checksIsFinalized, compares thePkgID against the caller's realm path, and re-derives the address. A forged
Objectreaches nothing honest use does not already reach.BankerTypeObjectSendis appended afterBankerTypeRealmIssue, so no existingconstant moves, and the
bankerstruct gains no field, so no persisted bankeris reinterpreted.
2. Lifecycle — answered, and pinned on a running chain
Realm.Timeis only ever incremented(
targetRlm.Time++inassignNewObjectID) and no deletion path decrementsit. A tick, once spent, is spent forever.
balance regardless, and the VM could not intervene if it wanted to: it cannot
see balances. So
NewObjectBankertakes anObjectsnapshot, not an object,and a realm can wind the balance down long after the object is gone.
object and keep no
reflect.Objectfor it, and the balance is strandedforever. There is no recovery path, and there should not be one — a recovery
path is a way for someone other than the minting realm to reach the address.
neither does this.
path (
GetPackageRealm→RunMemPackageOverRealm), so same path, same PkgID,same clock, and every address the old code minted stays funded and spendable.
/r/foo/v2is a different path, adifferent PkgID, a disjoint address space.
v1must drain, or stay callable.gno.land/pkg/integration/testdata/object_address_lifecycle.txtarruns thewhole thing against a real node: 700000ugnot sent to an object's address, the
object dropped from realm state, the balance still there, a new object minted
onto a different tick (
:18→:22) with an empty balance, and the deadobject's 700000ugnot drained out through the stored snapshot.
3. The growth rule — decided, not implemented
#6214 says the addressable set "is meant to grow, and to only ever grow", which
says when a change is allowed, not which change. The ADR replaces that with a
four-part admission test, and scores every candidate kind against it in a table:
TypedValue.Copyshares rather than clones.PkgID.IsRealmPkg().is one another kind already does.
Container pointers fail 1, slices fail 1, structs and arrays by value fail 2,
stdlib and
p/objects fail 3. Two of those have a visible route in, and underthe rule they become ordinary admissions rather than judgement calls.
4. #6026 and grc20 — not in this PR, with the reason #6214 never gave
#6214 calls it "a small follow-up: drop
NewToken's caller-suppliedseqid.IDand let
Token.Address()be its identity". It is not small, and the blocker is#6214's own stamping window.
NewTokenreturns a*Tokenallocated by the running call, soreflect.Ofreturns
ok == falseuntil the realm frame returns. And every GRC20 in thetree creates and uses its token in one transaction —
foo20,bar20,wugnot,grc20factory,test20, thetreasuryfiletests all doNewToken(...)thenMint(...)thengrc20reg.Register(...)insideinit.An address-backed
ID()panics at deploy for all of them. That is not amigration, it is a redeploy of every token on the chain plus an unexpressed new
authoring rule ("you may not mint in the transaction that creates the token").
The three obvious escapes are each worse than waiting: a lazily cached
ID()needs a write on a path reached from
Render, and gives the token one id in itscreation transaction and another afterwards; a two-phase
NewToken+Bindiscorrect but is an API burden imposed by a PR about the VM; emitting no id at
creation gives up the guarantee that makes token creation observable.
What unblocks it is separating "has an identity" from "is persisted" in the
VM, so an object can be stamped at allocation. Today
GetIsReal()isID.IsFinalized()andincRefCreatedDescendantsguards on the same predicate,so that means a third
ObjectInfostate and an audit of everyIsFinalized()call site. Its own change, its own risk, and the prerequisite the #6026
follow-up should be written against.
zrealm_reflect_duplicate_symbol.gno, from #6214, still demonstrates the endstate on a local token type, so the target is not in doubt. GRC721 has the same
exposure and the same blocker.
What is implemented vs. what the ADR only decides
chain/reflect, #6214's API, unchanged)banker.NewObjectBanker)ObjectDiff
22 files, +2725 / -3 against master
bc88c1ae. Of that, 17 files and 1862insertions are #6214 merged in unchanged as the base; this branch adds the
banker capability, two filetests, one integration txtar, the rewritten ADR, the
docs page section and the genesis hash re-pin.
Consequences worth a reviewer's attention
chain/bankernow importschain/reflect. genstd recomputesinitOrderfrom the import graph on its own; no manual ordering, no core change. Second
stdlib-to-stdlib import after
chain/runtime/unsafe→chain/runtime.NewObjectBanker's realm argument is last, matchingNewBanker(bt, rlm):a crossing function's first
realmparameter must be namedcur, and thisis not a crossing function.
expectedCrossrealm38Hashis re-pinned: stdlib.gnosources are genesis state, and bothreflect.gno(package comment) andbanker.gno(new function) changed.banker.go, the gas row and thegenerated binding are not committed state. crossrealm38 imports neither
package and its behaviour is unchanged.
chain/banker.objectBankerAddressat 1100, flat, draftedon an M1 against
chain/reflect.objectInfo's 1200 on the same run. Both arepending the reference-machine re-fit, like the rest of the table.
over one address, exactly as a realm banker is over the realm address, with a
strictly smaller blast radius — which also means a realm can now delegate
spending over one fund instead of over everything it holds.
Gate
See
.alt/GATE.mdin the branch for the exact commands and output.