Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

registry.gno

5.47 Kb · 146 lines
  1// Package registry is an open, on-chain map from a deployed gno package path
  2// back to the source that produced it: repository, commit, directory.
  3//
  4// It is the realm half of gno.land/p/moul/gnopm/v0, which holds the domain
  5// model and the whole of the reasoning; this file is wiring. Every exported
  6// mutation is a crossing function that resolves the caller, forwards to the
  7// library, aborts on error (the only way to revert state in gno) and emits an
  8// event for indexers.
  9//
 10// # It cannot promise, and does not pretend to
 11//
 12// Nothing here is verified and nothing here can be. A realm cannot clone a
 13// repository, so it cannot check that the bytes at the claimed commit are the
 14// bytes deployed at the claimed path. What it stores is testimony under a
 15// signature: this address says this package came from that source.
 16//
 17// That is still worth storing, for two reasons.
 18//
 19// First, it is testimony in the exact shape a verifier needs. Path, repository,
 20// commit and directory are precisely the four inputs to "hash the addpkg
 21// payload of that directory and compare it with what the chain hands back",
 22// which gnopm already does against a local tree (`gnopm verify -deployed`). The
 23// registry does not answer the question; it makes the question answerable by
 24// anything that can clone.
 25//
 26// Second, the chain knows who signed. A claim from the address that owns the
 27// package path's namespace comes from the party that controls the path, and
 28// OwnedBy reports that. It is computed on every read, never stored, because a
 29// name can be transferred and a stored answer would rot into exactly the kind
 30// of stale claim this realm exists to distinguish from a live one.
 31//
 32// # Open, and tagged
 33//
 34// Anyone may claim any path, including one they had nothing to do with. That is
 35// deliberate: gating registration on namespace ownership would leave the map
 36// empty on day one, when almost nothing has been registered by its own
 37// deployer, and an empty registry teaches nobody anything. A stranger's claim
 38// is not suppressed, it is labelled and ranked below the owner's, and Render
 39// never presents an unverified claim as a fact.
 40//
 41// A claimant may always withdraw their own claim, and may never touch anyone
 42// else's.
 43package registry
 44
 45import (
 46	"chain"
 47	"chain/runtime"
 48
 49	pm "gno.land/p/moul/gnopm/v0"
 50	"gno.land/r/sys/users"
 51)
 52
 53var reg = pm.New()
 54
 55// caller is the address that crossed into this realm. Deliberately not
 56// restricted to end users: a realm may hold a namespace, and a DAO registering
 57// the source of the packages it governs is the same operation.
 58func caller(cur realm) address {
 59	if !cur.IsCurrent() {
 60		panic("gnopm/registry: spoofed realm")
 61	}
 62	return cur.Previous().Address()
 63}
 64
 65func must(err error) {
 66	if err != nil {
 67		panic(err)
 68	}
 69}
 70
 71// resolveName is the r/sys/users half of the ownership rule, passed to the
 72// library so the library stays pure.
 73//
 74// isCurrent is deliberately ignored, matching r/moul/forge: a name that
 75// resolves through an alias still resolves to the same holder, so a namespace
 76// survives its owner renaming. IsDeleted folds the nil check in, which is what
 77// its own doc comment asks call sites to rely on.
 78func resolveName(name string) (address, bool) {
 79	data, _ := users.ResolveName(name)
 80	if data.IsDeleted() {
 81		return "", false
 82	}
 83	return data.Addr(), true
 84}
 85
 86// Register records that pkgPath was built from dir of repo at commit.
 87//
 88// Calling it again for the same path replaces the caller's own claim and
 89// nobody else's, so moving a claim to a new commit after a redeploy is one
 90// call with no read first.
 91//
 92// ref is optional and is never the thing verified: a ref moves, a commit does
 93// not. It is recorded so a reader can tell a claim pinned to a released tag
 94// from one pinned to a commit on nobody's branch, and so a verifier can report
 95// a commit that has since been orphaned by a force-push.
 96//
 97// dir is empty when the package sits at the repository root.
 98func Register(cur realm, pkgPath, repo, commit, dir, ref string) {
 99	a := caller(cur)
100	_, err := reg.Register(a, runtime.ChainHeight(), pkgPath, repo, commit, dir, ref)
101	must(err)
102	chain.Emit("SourceClaimed",
103		"pkgpath", pkgPath,
104		"claimant", a.String(),
105		"repo", repo,
106		"commit", commit,
107	)
108}
109
110// Withdraw removes the caller's own claim about pkgPath.
111func Withdraw(cur realm, pkgPath string) {
112	a := caller(cur)
113	must(reg.Withdraw(a, pkgPath))
114	chain.Emit("SourceWithdrawn", "pkgpath", pkgPath, "claimant", a.String())
115}
116
117// PackageCount is how many package paths carry at least one claim.
118func PackageCount() int { return reg.Size() }
119
120// ClaimCount is the total number of claims across every path.
121func ClaimCount() int { return reg.Claims() }
122
123// HasClaims reports whether anything has been said about pkgPath. Cheap enough
124// for another realm or an indexer to ask per package.
125func HasClaims(pkgPath string) bool { return reg.Package(pkgPath) != nil }
126
127// OwnerClaim returns the repository, commit and directory claimed by the party
128// that owns pkgPath's namespace, and whether such a claim exists.
129//
130// This is the only read that filters, and it filters on the one signal the
131// chain can actually prove. Everything else a caller wants is in Render or in
132// the events.
133func OwnerClaim(pkgPath string) (repo, commit, dir string, ok bool) {
134	p := reg.Package(pkgPath)
135	if p == nil {
136		return "", "", "", false
137	}
138	p.IterateClaims(func(c *pm.Claim) bool {
139		if c.OwnedBy(resolveName) {
140			repo, commit, dir, ok = c.Repo, c.Commit, c.Dir, true
141			return true // stop
142		}
143		return false
144	})
145	return repo, commit, dir, ok
146}