read.gno
3.93 Kb · 146 lines
1package nsvote
2
3import (
4 "strings"
5
6 "gno.land/p/nt/ufmt/v0"
7 "gno.land/r/nym-mnsrehearsal001/nsdata/v2"
8)
9
10// Everything a caller needs about one request, on one line. Fields are
11// separated by "|" rather than "," because a reason may well contain a
12// comma; it may not contain "|" or a newline, which Propose enforces.
13func row(id int64, p *Proposal) string {
14 kind := p.kind
15 if kind == "" {
16 kind = "domain" // anything written before kinds existed
17 }
18 return ufmt.Sprintf("%d|%s|%s|%s|%s|%d|%d|%d|%s|%d",
19 id, kind, p.label, p.proposer.String(), p.reason,
20 p.created, p.yes, p.no, p.status, p.decided)
21}
22
23// ListProposals pages newest first, which is the order anybody actually
24// wants to read them in.
25func ListProposals(after string, limit int) (rows, next string) {
26 if limit <= 0 || limit > maxPage {
27 limit = maxPage
28 }
29 var keys []string
30 proposals.Iterate("", "", func(k string, _ any) bool {
31 keys = append(keys, k)
32 return false
33 })
34
35 var b strings.Builder
36 n := 0
37 last := ""
38 started := after == ""
39 // Walk backwards for newest-first; `after` is the last key returned.
40 for i := len(keys) - 1; i >= 0; i-- {
41 k := keys[i]
42 if !started {
43 if k == after {
44 started = true
45 }
46 continue
47 }
48 if n >= limit {
49 return b.String(), last
50 }
51 v := proposals.Get(k)
52 if v == nil {
53 continue
54 }
55 if b.Len() > 0 {
56 b.WriteString("\n")
57 }
58 p := v.(*Proposal)
59 b.WriteString(row(parseKey(k), p))
60 last = k
61 n++
62 }
63 return b.String(), ""
64}
65
66func parseKey(k string) int64 {
67 var out int64
68 for i := 0; i < len(k); i++ {
69 if k[i] < '0' || k[i] > '9' {
70 return 0
71 }
72 out = out*10 + int64(k[i]-'0')
73 }
74 return out
75}
76
77func GetProposal(id int64) string {
78 v := proposals.Get(key(id))
79 if v == nil {
80 return ""
81 }
82 return row(id, v.(*Proposal))
83}
84
85func CountProposals() int64 { return int64(proposals.Size()) }
86
87// HasVoted lets the page grey out a button instead of letting somebody
88// sign a transaction that is certain to be rejected.
89func HasVoted(id int64, voter address) string {
90 v := votes.Get(voteKey(id, voter))
91 if v == nil {
92 return ""
93 }
94 return v.(string)
95}
96
97// CanTakePart answers the one question the page needs before it offers
98// any of this: does this address hold a name.
99func CanTakePart(who address) bool { return nsdata.BalanceOf(who) > 0 }
100
101// Roster is the raw material for the leaderboards: one line per name,
102// owner first, so the caller can aggregate however it likes.
103//
104// It lives here rather than in nslogic because this realm is being
105// deployed anyway and nslogic is not — adding it there would have cost
106// a fresh deployment of the largest realm we have, for a read.
107//
108// Paged, and the page walks every name in it. That is fine at the scale
109// this registry is at and it will not be fine at fifty thousand names;
110// at that point the aggregate belongs somewhere it is kept as it
111// changes rather than recomputed on every read.
112func Roster(after string, limit int) (rows, next string) {
113 if limit <= 0 || limit > maxPage {
114 limit = maxPage
115 }
116 keys, nxt := nsdata.ExportNames("", after, limit)
117
118 var b strings.Builder
119 for _, k := range strings.Split(keys, ",") {
120 if k == "" {
121 continue
122 }
123 at := strings.Index(k, "*")
124 if at < 0 {
125 continue
126 }
127 domainLabel, label := k[:at], k[at+1:]
128 owner, registered, expires, streak, frozen, _ := nsdata.GetNameInfo(label, domainLabel)
129 if b.Len() > 0 {
130 b.WriteString("\n")
131 }
132 b.WriteString(ufmt.Sprintf("%s|%s*%s|%d|%d|%d|%t",
133 owner.String(), label, domainLabel, registered, expires, streak, frozen))
134 }
135 return b.String(), nxt
136}
137
138// Version lets a caller find out what it is talking to BEFORE it sends a
139// transaction that would be rejected for having the wrong number of
140// arguments.
141//
142// v1 has no such function, so a nil answer is itself the answer: an
143// older realm. That is cheaper and more honest than the site assuming
144// the newest shape and failing at signing time, which is exactly what
145// happened when Propose grew a `kind`.
146func Version() string { return "v2" }