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.

read.gno

8.65 Kb · 246 lines
  1package nsdata
  2
  3import (
  4	"strings"
  5	"time"
  6
  7	"gno.land/r/nym-mnsrehearsal001/nsnft/v2"
  8)
  9
 10// Plain reads — no `cur realm` parameter, no authentication, since there
 11// is no caller identity to check on a read. Everything here returns raw
 12// stored values; formatting them for display is the view realm's job,
 13// not the vault's. (Status strings like "VALID (363 days left)" used to
 14// live here. They were display logic in a realm that can never be
 15// redeployed, so they moved to nsview.)
 16
 17func ListDomains() string {
 18	var b strings.Builder
 19	first := true
 20	domains.Iterate("", "", func(key string, v any) bool {
 21		if !first {
 22			b.WriteString(",")
 23		}
 24		b.WriteString(key)
 25		first = false
 26		return false
 27	})
 28	return b.String()
 29}
 30
 31// ListNames and CountNames scan only the key span belonging to
 32// `domainLabel` (see domainRange in auth.gno), costing O(names in that
 33// domain) rather than O(every name in the registry).
 34func ListNames(domainLabel string) string {
 35	var b strings.Builder
 36	first := true
 37	start, end := domainRange(domainLabel)
 38	// The label is the part of the key after "domain*". Records no
 39	// longer carry their own label — the key already held it, and
 40	// storing it twice cost storage forever to say the same thing.
 41	cut := len(domainLabel) + 1
 42	names.Iterate(start, end, func(key string, v any) bool {
 43		if !first {
 44			b.WriteString(",")
 45		}
 46		b.WriteString(key[cut:])
 47		first = false
 48		return false
 49	})
 50	return b.String()
 51}
 52
 53func CountNames(domainLabel string) int64 {
 54	var count int64
 55	start, end := domainRange(domainLabel)
 56	names.Iterate(start, end, func(key string, v any) bool {
 57		count++
 58		return false
 59	})
 60	return count
 61}
 62
 63func CountDomains() int64 { return int64(domains.Size()) }
 64
 65func DomainExists(label string) bool            { _, ok := getDomain(label); return ok }
 66func NameExists(label, domainLabel string) bool { _, ok := getName(label, domainLabel); return ok }
 67
 68func GetDomainOwner(label string) address { return mustGetDomain(label).owner }
 69
 70// GetNameOwner asks the ledger. It still panics for an unknown NAME, as it
 71// always did, so callers that relied on that are unchanged.
 72func GetNameOwner(label, domainLabel string) address {
 73	mustGetName(label, domainLabel)
 74	return nsnft.OwnerOf(nameTokenID(label, domainLabel))
 75}
 76
 77// GetDomainInfo / GetNameInfo return a whole record in one call, so a
 78// view realm can render a page without six round trips.
 79// Timestamps come back as unix SECONDS. They used to be time.Time,
 80// which vm/qeval rendered using gno's internal epoch, so every consumer
 81// had to know to subtract 62135596800 — our own frontend did exactly
 82// that. Returning unix seconds stops leaking that offset into every
 83// integration, and it is what the records now hold anyway.
 84//
 85// `streak` is computed from the registration date rather than read from
 86// a stored field. See streakOf.
 87func GetDomainInfo(label string) (owner address, registered, expires, streak int64, frozen bool) {
 88	d := mustGetDomain(label)
 89	return d.owner, d.registered, d.expires, streakOf(d.registered, time.Now().Unix()), d.frozen
 90}
 91
 92/*
 93GetNameInfo keeps its six-value shape so every existing reader — nslogic,
 94nsview, nsskin, nsvote, the site, both snapshot tools — goes on parsing it
 95unchanged. Two of the six are now answered differently:
 96
 97	owner  is read from nsnft, never stored here. The snapshot tools record
 98	       whatever this returns, so they now record the real owner — the
 99	       restore-hands-sold-names-back bug the first attempt had cannot
100	       happen, because there is no stale copy for them to read.
101
102	paid   replaces isNFT and means exactly one thing: the name is NOT
103	       held as an un-upgraded free-tier record. It is deliberately not
104	       "transferable" — a frozen paid name is locked too, and reading
105	       the lock would have made a frozen paid name look free, which is
106	       precisely the proxy bug that let free renewals renew paid names.
107*/
108func GetNameInfo(label, domainLabel string) (owner address, registered, expires, streak int64, frozen, paid bool) {
109	n := mustGetName(label, domainLabel)
110	tid := nameTokenID(label, domainLabel)
111	return nsnft.OwnerOf(tid), n.registered, n.expires,
112		streakOf(n.registered, time.Now().Unix()), n.frozen,
113		nsnft.LockBits(tid)&(1<<lockBitFree) == 0
114}
115
116// GetNameTTL / GetDomainTTL report how long a resolver may cache an
117// answer about this record, in seconds, where 0 MEANS NEVER CACHE — the
118// ENS registry's rule, deliberately, so integrators need not read
119// anything of ours to handle it correctly.
120// A TTL written for a previous holder is not this holder's cache policy.
121// A stale one reads as 0 — never cache — which is the safe answer during
122// exactly the window after a sale when a cached answer pays the seller.
123func GetNameTTL(label, domainLabel string) int64 {
124	n := mustGetName(label, domainLabel)
125	if !isFresh(n, nameTokenID(label, domainLabel)) {
126		return 0
127	}
128	return n.ttl
129}
130func GetDomainTTL(label string) int64 { return mustGetDomain(label).ttl }
131
132// GetNameHeldSince is when the current holder's holding began, in unix
133// seconds — or 0 when the ledger has moved on since this registry last
134// saw the record (a sale it has not yet observed). It can understate a
135// holding, never overstate one. See Name.since.
136func GetNameHeldSince(label, domainLabel string) int64 {
137	n := mustGetName(label, domainLabel)
138	if !isFresh(n, nameTokenID(label, domainLabel)) {
139		return 0
140	}
141	return n.since
142}
143
144// RestoredFor reports whom a restore wrote this record for, or empty.
145// See Name.restoredFor.
146func RestoredFor(label, domainLabel string) address {
147	return mustGetName(label, domainLabel).restoredFor
148}
149
150// IsNamePaid reports that a name is not held as an un-upgraded free-tier
151// record. See GetNameInfo on why this is not "transferable".
152func IsNamePaid(label, domainLabel string) bool {
153	mustGetName(label, domainLabel)
154	return nsnft.LockBits(nameTokenID(label, domainLabel))&(1<<lockBitFree) == 0
155}
156
157func IsDomainValid(label string) bool {
158	d, ok := getDomain(label)
159	return ok && isValidNow(d.expires, time.Now().Unix())
160}
161
162func IsNameValid(label, domainLabel string) bool {
163	n, ok := getName(label, domainLabel)
164	return ok && isValidNow(n.expires, time.Now().Unix())
165}
166
167// -- extension slots --
168
169func GetDomainExtra(label, key string) string {
170	v := mustGetDomain(label).extra.Get(key)
171	if v == nil {
172		return ""
173	}
174	return v.(string)
175}
176
177/*
178GetNameExtra answers EMPTY for data written in a previous ownership epoch.
179
180This is the read half of the lazy wipe. A sale that settles on nsnft —
181through our marketplace or anybody else's — never calls this realm, so
182nothing here can clear the seller's payment addresses at the moment it
183happens. What this CAN do is refuse to repeat them: the instant the
184ledger's epoch moves, every extra on the name stops answering, for every
185reader, before anybody has written anything.
186
187The write half, which destroys the stale data rather than hiding it, is
188freshen in write.gno. Both halves are needed: hiding alone is what the
189first attempt shipped, and the buyer's first edit brought the seller's
190Bitcoin address back as the buyer's.
191*/
192func GetNameExtra(label, domainLabel, key string) string {
193	n := mustGetName(label, domainLabel)
194	if !isFresh(n, nameTokenID(label, domainLabel)) {
195		return ""
196	}
197	v := n.extra.Get(key)
198	if v == nil {
199		return ""
200	}
201	return v.(string)
202}
203
204func GetGlobal(key string) string {
205	v := globals.Get(key)
206	if v == nil {
207		return ""
208	}
209	return v.(string)
210}
211
212// GetAccount reads one field of an address's own record, or "" when it
213// has none. See `accounts` in types.gno.
214func GetAccount(owner address, key string) string {
215	v := accounts.Get(accountKey(owner, key))
216	if v == nil {
217		return ""
218	}
219	return v.(string)
220}
221
222// GetPrimary is a raw pointer lookup. It reports what an address last
223// SELECTED, not what it still owns — callers presenting this as reverse
224// resolution must re-check ownership (see nslogic.ResolveAddress).
225func GetPrimary(owner address) string {
226	v := primary.Get(owner.String())
227	if v == nil {
228		return ""
229	}
230	return v.(string)
231}
232
233// -- config reads --
234
235func GetTreasury() address                { return treasury }
236func GetTermLength() int64                { return termLength }
237func GetGracePeriod() int64               { return gracePeriod }
238func GetMaxDeletePerCall() int            { return maxDeletePerCall }
239func GetLabelPattern() string             { return labelPattern }
240func GetLabelLength() (min, max int)      { return minLabelLen, maxLabelLen }
241func GetTokenURIBase() string             { return tokenURIBase }
242func GetExtraLimits() (k, v, entries int) { return maxExtraKeyLen, maxExtraValueLen, maxExtraEntries }
243func GetGlobalEntryLimit() int            { return maxGlobalEntries }
244func GetAccountLimits() (k, v, entries int) {
245	return maxAccountKeyLen, maxAccountValueLen, maxAccountEntries
246}