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.

genesis.gno

5.73 Kb · 163 lines
  1package nsdata
  2
  3import "gno.land/p/nt/avl/v0"
  4
  5/*
  6GENESIS — the state a registry is born with.
  7
  8WHY IT LIVES IN THE PACKAGE. This project has been through three chains
  9(topaz-1, sapphire-1, pearl-1), and every time the price list, the
 10referral tiers and the domain list existed only as somebody's memory of an
 11afternoon spent in the admin panel. A value set by hand after deployment
 12is a value that is lost with the chain. A value in this file ships with
 13the realm.
 14
 15WHY nsdata AND NOT nslogic. This realm owns the state it writes, so init
 16can write it with no permission from anybody. nslogic cannot: it writes
 17through this realm's trust gate, and trust is granted by a timelocked
 18proposal AFTER deployment — an init in nslogic would panic before the
 19grant existed and take the deploy down with it.
 20
 21WHY DOMAINS CAN BE CREATED HERE AT ALL. They used to be tokens, and
 22minting one needs nsnft to trust this realm, which it cannot yet do during
 23init. Domains are records now (MIGRATION.md, rule 7), so creating one is a
 24tree write and nothing more.
 25
 26WHAT IS NOT HERE. Names. No holder's records carry from one chain to the
 27next — that is the owner's decision (see the mainnet checklist): testnet
 28wallets get an entitlement, not their old records. And nothing that names
 29a generation — the token URI base, the lab domain — because this file is
 30meant to be the same for the sandbox and for mainnet.
 31
 32EVERY VALUE IS VALIDATED AND A BAD ONE PANICS. A genesis file that quietly
 33skipped a malformed global would deploy a registry that looks configured
 34and is missing its price list, and nothing would say so until somebody was
 35charged the fallback. Failing the deploy costs one retry.
 36
 37The data itself is generated into genesis_data.gno by
 38tools/freeze-genesis.py, from a snapshot of a chain that was configured by
 39hand. Edit the chain, snapshot it, regenerate; do not edit the data file.
 40*/
 41
 42// Genesis domains get a long runway. They are project-owned and all
 43// created in the same block, so a single term would have every one of them
 44// lapse on the same day a year later — and a lapsed domain refuses every
 45// registration under it. Renewing them is still the admin's job; this
 46// just stops the first renewal being a cliff.
 47const genesisDomainTerms = 10
 48
 49/*
 50refusedInGenesis names what may never be baked into a registry's birth.
 51
 52Anything whose VALUE is an address: a key that holds power — the free-tier
 53granter was the one that shipped by accident — is set deliberately, after
 54deploy, on the chain it belongs to, never inherited from whatever chain a
 55snapshot was taken on. And the lab nomination, which belongs to a sandbox
 56generation and would otherwise let a hand-edited data file ship a lab on
 57a real domain.
 58
 59No regexp: one compiled pattern costs more storage than this whole file.
 60*/
 61func refusedInGenesis(key, value string) bool {
 62	if key == "labdomain" || key == "granter" {
 63		return true
 64	}
 65	return looksLikeAddress(value)
 66}
 67
 68func looksLikeAddress(v string) bool {
 69	if len(v) != 40 || v[0] != 'g' || v[1] != '1' {
 70		return false
 71	}
 72	for i := 2; i < len(v); i++ {
 73		c := v[i]
 74		if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
 75			return false
 76		}
 77	}
 78	return true
 79}
 80
 81type genesisCfg struct {
 82	termLength, gracePeriod                           int64
 83	minLabelLen, maxLabelLen                          int
 84	maxExtraKeyLen, maxExtraValueLen, maxExtraEntries int
 85	maxDeletePerCall                                  int
 86}
 87
 88type genesisDomain struct {
 89	label string
 90	extra [][2]string
 91}
 92
 93func applyGenesis(deployer address, now int64) {
 94	// Limits first, so every extra written below is held to them.
 95	c := genesisConfig()
 96	if c.maxExtraKeyLen > 0 {
 97		maxExtraKeyLen = c.maxExtraKeyLen
 98	}
 99	if c.maxExtraValueLen > 0 {
100		maxExtraValueLen = c.maxExtraValueLen
101	}
102	if c.maxExtraEntries > 0 {
103		maxExtraEntries = c.maxExtraEntries
104	}
105	if c.maxDeletePerCall > 0 {
106		maxDeletePerCall = c.maxDeletePerCall
107	}
108	if c.minLabelLen > 0 {
109		minLabelLen = c.minLabelLen
110	}
111	if c.maxLabelLen > 0 {
112		maxLabelLen = c.maxLabelLen
113	}
114	if c.termLength > 0 {
115		termLength = c.termLength
116	}
117	if c.gracePeriod > 0 {
118		gracePeriod = c.gracePeriod
119	}
120	// The one invariant the lifecycle setters guard, guarded here too:
121	// anchoring a renewal to its previous due date only makes sense while a
122	// renewal cannot land more than one term late.
123	if gracePeriod > termLength {
124		panic("nsdata genesis: grace period is longer than the term")
125	}
126
127	for _, kv := range genesisGlobals() {
128		for _, r := range reservedGlobals {
129			if kv[0] == r {
130				panic("nsdata genesis: " + r + " belongs to the restore machinery")
131			}
132		}
133		if refusedInGenesis(kv[0], kv[1]) {
134			panic("nsdata genesis: " + kv[0] + " holds an address or names a generation; set it after deploy, deliberately")
135		}
136		if err := setGlobal(kv[0], kv[1]); err != nil {
137			panic("nsdata genesis: global " + kv[0] + ": " + err.Error())
138		}
139	}
140
141	for _, g := range genesisDomains() {
142		if err := assertLabelKeySafe(g.label); err != nil {
143			panic("nsdata genesis: domain " + g.label + ": " + err.Error())
144		}
145		if len(g.label) < minLabelLen || len(g.label) > maxLabelLen {
146			panic("nsdata genesis: domain " + g.label + " is outside the label length limits")
147		}
148		if _, dup := getDomain(g.label); dup {
149			panic("nsdata genesis: domain " + g.label + " is listed twice")
150		}
151		// Owned by the deployer, like every domain the admin registers.
152		// Under `gno test` there is no deployer, and the domains are still
153		// created so the tests exercise the same registry a chain gets.
154		d := &Domain{owner: deployer, registered: now,
155			expires: now + genesisDomainTerms*termLength, extra: avl.Tree{}}
156		for _, kv := range g.extra {
157			if err := setExtra(&d.extra, kv[0], kv[1]); err != nil {
158				panic("nsdata genesis: domain " + g.label + " key " + kv[0] + ": " + err.Error())
159			}
160		}
161		domains.Set(g.label, d)
162	}
163}