governor.gno
94.94 Kb · 2303 lines
1package governor
2
3import (
4 "chain"
5 "crypto/sha256"
6 "strconv"
7 "strings"
8
9 bptree "gno.land/p/nt/bptree/v0"
10 ufmt "gno.land/p/nt/ufmt/v0"
11)
12
13// A proposal is a KIND and a PAYLOAD, both strings.
14//
15// Forced, and better than the alternative. MsgCall.Args is []string
16// (gno.land/pkg/sdk/vm/msgs.go) and convertArgToGno switches on
17// gno.BaseOf(argT), so scalars and named scalars like `address` convert; a
18// struct, interface, pointer or func panics with "unexpected type in contract
19// arg". An entrypoint taking one is uncallable by transaction.
20//
21// A []byte converts too, base64-decoded, so the string is a choice: a payload
22// arrives as text a voter can read, where bytes would arrive as base64.
23//
24// A proposer cannot supply a closure either. A realm can persist one, but
25// MsgRun forces the ephemeral package private and the save walk refuses it
26// (realm.go, "cannot persist function or method from the private realm").
27//
28// The property that falls out is what OpenZeppelin cannot offer: Describe and
29// Do consume the identical string, so what voters read IS what runs.
30type Kind interface {
31 // Name is the registry key.
32 Name() string
33
34 // Describe renders a payload for the people voting on it.
35 //
36 // It must be a total function of the payload ALONE. Read live state here
37 // and two voters at different heights are deciding different questions
38 // while looking at the same page.
39 Describe(payload string) string
40
41 // Check validates a payload against the world as it is now. Called when
42 // the proposal opens AND again immediately before Do.
43 //
44 // The second call is the point. A proposal that was sound a week ago need
45 // not be sound now, and the alternative is discovering that inside Do,
46 // half-applied.
47 Check(payload string) error
48
49 // Do performs it. Non-crossing, taking the realm as data, so a /p/ package
50 // could implement this unchanged — crossing functions are illegal outside
51 // a realm.
52 //
53 // Returning an error and panicking are different decisions. An error
54 // FINISHES the proposal: recorded failed, slot returned, no retry. Say so
55 // when the answer will not change.
56 //
57 // A panic ABORTS the transaction, writing nothing — including the failure
58 // — so the proposal stays Succeeded and anybody may try again. Say so when
59 // a retry could work, or when half-finishing is worse than not starting:
60 // an abort is the only rollback gno offers. govern:batch depends on it.
61 Do(_ int, rlm realm, payload string) error
62}
63
64// Rules are per kind, never global. Renaming a parameter and spending the
65// reserve should not need the same turnout, which is why one global quorum is
66// always wrong for something.
67//
68// Basis points as int64, never float64: gno's own govdao compares against a
69// float 66.66, and a threshold decided by binary floating point is a rounding
70// argument waiting to happen. yes*bps >= (yes+no)*ThresholdBps is exact.
71//
72// bps is named once because it is load-bearing in three places at once: the
73// tally, the rendered percentages and the supply ceiling. A scale written out
74// separately in each is a number that can come to disagree with itself.
75const bps = int64(10000)
76
77// maxWeighable is the largest snapshot supply a tally can weigh without yes*bps
78// overflowing int64 (and going negative, which reads a won vote as lost). The
79// Electorate contract requires PastTotal to stay within it; grc20votes caps its
80// own supply here exactly, so for that token this ceiling is never approached. It
81// is checked at the Propose door for a swapped-in electorate that does not.
82const maxWeighable = int64(9223372036854775807) / bps
83
84type Rules struct {
85 QuorumBps int64 // of the snapshot supply, counting abstain as turnout
86
87 // ThresholdBps is the share of yes+no (abstain excluded) that yes must reach.
88 // The comparison is INCLUSIVE — yes*bps >= (yes+no)*ThresholdBps — so at 5000
89 // a 50-50 tie PASSES. A realm wanting a strict majority sets 5001 or more; the
90 // bootstrap and gno's govdao use a 6600 supermajority.
91 ThresholdBps int64
92
93 // ProposeBps is what a proposer must hold, as a fraction of the snapshot
94 // supply, to open a question at all.
95 //
96 // Attention control, not spam control: opening a proposal already costs a
97 // new key's worth of deposit. The list is what holders read, and a list
98 // nobody reads is a governor nobody governs. Zero means anyone may
99 // propose — right for a small realm, wrong for a public token.
100 ProposeBps int64
101 VotingBlocks int64
102 DelayBlocks int64 // succeeded -> executable; the timelock
103 GraceBlocks int64 // executable -> expired
104}
105
106const (
107 stateActive int8 = iota
108 stateDefeated
109 stateSucceeded
110 stateExecuted
111 stateFailed
112 stateCanceled
113 stateExpired
114)
115
116// proposal is one question, frozen.
117type proposal struct {
118 id int64
119 kind string
120 payload string
121 title string
122 proposer address
123
124 // Both copied when the proposal opens: the rules, so a later change cannot
125 // move the bar earlier voters faced; the snapshot epoch, so the electorate
126 // is the one that existed when the question was asked.
127 rules Rules
128 epoch uint32
129 total int64 // supply at that epoch, so quorum has a fixed denominator
130
131 // engaged is the quorum's DENOMINATOR; total is everything in issue. Equal
132 // for this realm's token and not necessarily for another: an electorate may
133 // drop idle weight out of the bar while that weight can still vote. So
134 // quorum divides by engaged, and "what could still be cast" divides by
135 // total.
136 engaged int64
137
138 // quorumFloor, when positive, is an ABSOLUTE turnout the proposal needs
139 // instead of the rules' QuorumBps fraction of engaged — a figure the consumer
140 // computed itself (a court's max(5% supply, min(1×X̄, ⅓ votable))) and passed
141 // through ProposeWithQuorum. Snapshotted here like every other bar, so it
142 // cannot move under an open vote. Zero means the bps formula applies, which is
143 // every ordinary caller.
144 quorumFloor int64
145
146 opened int64
147 closes int64
148 // The same two moments in unix seconds, written beside the heights and never
149 // derived afterwards. A VOTING WINDOW IS A PROMISE TO A VOTER, and a height
150 // only means a date if the chain's pace never changes; a proposal opened
151 // before these existed carries 0 and keeps the height it was opened under.
152 openedTime int64
153 closesTime int64
154 ready int64 // set when it succeeds
155 readyTime int64 // its wall-clock twin, written at the same moment
156 state int8
157 reason string
158
159 yes, no, abstain int64
160 // voted exists only to refuse a second vote from the same address. Nothing
161 // renders it and nothing else reads it.
162 //
163 // A tree per proposal rather than one shared tree keyed by (id, voter),
164 // for cleanup rather than cost: this buys a whole node on the first vote
165 // (~4,500 bytes) where a shared tree would cost one entry, but dropping it
166 // is one assignment and the deposit goes back to whoever settles. Clearing
167 // a range out of a shared tree is a removal per voter, unbounded, and
168 // bptree forbids removing during iteration.
169 //
170 // Dropped the moment the proposal closes: Vote refuses a closed proposal
171 // before it looks here, so the tree would be rent on something nobody can
172 // consult. The roll survives in the Voted events.
173 voted *bptree.BPTree
174}
175
176// entry is a kind and whether it may actually be proposed. Separate, because
177// publishing code is something a realm does and letting that code hold this
178// governor's authority is something the holders do. One registration call fuses
179// them, and then whoever may register can grant themselves any power with no
180// vote.
181type entry struct {
182 kind Kind
183 rules Rules
184 live bool
185}
186
187// maxLive bounds how many questions can be open at once. A security parameter,
188// not a storage one: it is the only thing between a determined proposer and a
189// list nobody can read.
190//
191// Cancel frees a slot outright. Time only makes one RECLAIMABLE — a read can
192// work out that a proposal is over but cannot write it down — so the slot
193// comes back when somebody calls Settle, or when sweep finds it on the next
194// Propose. Reading this as "time frees a slot" is how the reclaim came to be
195// something a proposer could hold shut; see the cursor sweep keeps.
196//
197// The open section of the front page has no length cap of its own, so its
198// worst case is this many rows at maxTitle and maxKindName each: 14,220 bytes,
199// which is a page. Ten thousand slots would be 2.2MB, which is not. A test
200// holds the product against a ceiling, so raising either number fails it.
201const maxLive = 64
202
203// govLanes is how many of those slots only the governor's own kinds may take.
204//
205// Without it the sixty-four are a commons and any one kind may hold all of
206// them. Sixty-four proposals of some cheap kind with a long voting window, and
207// nothing else can be asked until that window runs out — including the
208// govern:rules vote that would shorten the window. The realm stays up and stops
209// being governable, which is the one failure it must not have, because every
210// other kind of congestion is something the holders can vote their way out of.
211//
212// Priced rather than assumed: the bootstrap terms put ProposeBps at zero, so
213// any holder may propose, and sixty-four proposals is about 56 GNOT of deposit
214// — refundable, and held for one bootstrap voting window of a week.
215//
216// Eight rather than one. The governor has five kinds of its own and they are
217// not alternatives: retuning a kind, adopting another, and retiring a third can
218// all be live questions at once, and a lane of one would mean the first of them
219// blocks the rest. Eight leaves room for a second round of each.
220//
221// This does NOT stop the governor's own kinds crowding each other out, and it
222// is not meant to. A realm whose holders have filled the governance lane with
223// governance is congested by its own doing and can wait; a realm shut out of
224// governance by a kind it adopted for something else cannot.
225const govLanes = 8
226
227// maxOpen is what any other kind may fill, which is the number a proposer
228// actually meets. maxLive stays the bound on the front page, since the lane is
229// a reallocation of the sixty-four and not an addition to them.
230const maxOpen = maxLive - govLanes
231
232var (
233 errNoSuchKind = govErr("no kind by that name has been offered")
234 errAlreadyLive = govErr("that kind is already adopted")
235 errNotLive = govErr("that kind is not adopted")
236 errCannotRetireBuiltin = govErr("the governor's own kinds cannot be retired")
237)
238
239// Governor is one governing body: which powers it has adopted, every question
240// it has been asked, and where its slot reclaim had got to.
241//
242// Allocated by the consuming realm, which is what makes this a library rather
243// than a realm. A /p/ package's own state is frozen after init, so nothing
244// durable could live at package level; the trees below carry the consumer's
245// storage stamp, and every method here borrows the consumer's authority back
246// for the write.
247//
248// Every field is unexported and no method hands one out. /p/-declared types can
249// be named by other /p/ packages, so an exported *proposal or a method
250// returning one would let a stranger declare a mutator over it — and the
251// storage-realm borrow would run that mutator under the CONSUMING realm's
252// authority. See gno-security-guide.md §3(B) and §4.
253type Governor struct {
254 voters Electorate
255 token Token
256
257 kinds *bptree.BPTree // name -> *entry
258 proposals *bptree.BPTree // enc(id) -> *proposal
259 openIdx *bptree.BPTree // digest -> enc(id), the live ones
260
261 propSeq int64
262
263 // executing is the re-entrancy latch, and it is held for the WHOLE of every
264 // state-changing call — propose, castVote and Execute — not only Execute.
265 //
266 // It started as an Execute-only flag, against a kind calling back in while
267 // its own Do runs and seeing a proposal mid-execution. The other two read it
268 // and never wrote it, which made those reads decorative: propose and castVote
269 // both call into the consumer-supplied Electorate (PastVotes, PastTotal,
270 // EngagedTotal, and Height/Now by way of settle) and both mutate afterwards,
271 // so a hostile or merely re-entrant electorate could nest a second call
272 // inside the first. castVote was the sharp one — its already-voted guard
273 // reads state that the same function does not write until after the external
274 // call, so a nested frame passed both checks and the tally took the weight
275 // twice.
276 //
277 // The name is kept because every panic and every test says it; what changed
278 // is the span, from one call to three.
279 //
280 // THE THREE PANICS GAINED A CLAUSE RATHER THAN NEW WORDING. Each said "from
281 // inside an execution", which stopped being the only cause the moment vote
282 // and propose began holding the latch too. Rewriting them outright broke
283 // TestExecutionCannotReEnterTheGovernor, which pins each message by
284 // substring — correctly, because a panic string is API for anyone matching on
285 // it. Appending "or a vote" makes them true without moving what was pinned.
286 executing bool
287
288 // sweepFrom is where the next slot reclaim starts scanning, and it rotates.
289 // The index is keyed by digest(kind, payload) and a proposer chooses their
290 // payload, so they choose their key: park a few long-running proposals on
291 // the lowest keys and a FIXED window never sees past them, freeing nothing
292 // while the rest of the list is finished business. Beating the lowest of
293 // fifty-six random keys is a few hundred hashes offline.
294 sweepFrom string
295}
296
297// reserved names the governor's own kinds. Offer refuses this prefix, so no
298// realm can publish something that renders as a built-in.
299const reserved = "govern:"
300
301// isReserved reports whether a name belongs to the governor itself.
302//
303// One predicate, two callers: Offer refusing to publish under the prefix, and
304// govern:retire refusing to withdraw something carrying it. It was written
305// twice, and a weakened retire copy would let the holders withdraw
306// govern:adopt — after which no kind could ever be adopted again and no
307// proposal could restore it.
308func isReserved(name string) bool {
309 return len(name) >= len(reserved) && name[:len(reserved)] == reserved
310}
311
312// NewRules builds the terms a kind would pass on. A realm cannot allocate
313// another realm's struct — govern.Rules{...} written elsewhere is refused with
314// "cannot allocate ... in realm" — so the value comes into existence here and
315// the caller passes numbers. Without it, Offer was uncallable from outside this
316// package.
317func NewRules(quorumBps, thresholdBps, votingBlocks, delayBlocks, graceBlocks, proposeBps int64) Rules {
318 return Rules{
319 QuorumBps: quorumBps, ThresholdBps: thresholdBps,
320 VotingBlocks: votingBlocks, DelayBlocks: delayBlocks,
321 GraceBlocks: graceBlocks, ProposeBps: proposeBps,
322 }
323}
324
325// Offer shelves a kind. It takes effect on nothing.
326//
327// Ungated, because an offered kind can do nothing. What it buys is a name
328// pointing at published, immutable code the holders are later asked to approve.
329//
330// The two-step exists because MsgCall.Args is []string: an account cannot hand
331// over a Kind, only a realm can construct one. If adopting required passing
332// the value, only realms could grow the governor — and that decision has to
333// belong to the holders. Offer is the realm's half; the vote is theirs.
334func (g *Governor) Offer(who address, k Kind, r Rules) {
335 name := k.Name()
336 mustBeUsableName(name)
337 if isReserved(name) {
338 panic("govern: that prefix belongs to the governor")
339 }
340 g.mustBeSaneRules(r)
341
342 // A name, once offered, is bound to that code forever.
343 //
344 // The alternative — allowing a re-offer while the kind is not yet adopted —
345 // is a live attack, not an untidiness. Offer harmless code, wait for an
346 // adoption proposal to open on it, then re-offer under the same name while
347 // the vote runs. The holders read the first Describe, approve it, and Do
348 // dispatches to the second. gno's own daokit has this shape and records it
349 // as a TODO; here the vote would be a signature on a blank cheque.
350 //
351 // Pinning the value into the proposal would also close it, at the cost of
352 // a Kind stored per proposal and a rule about which copy wins. Refusing
353 // the rewrite is smaller and matches how the chain already behaves: a
354 // realm cannot be redeployed at its path either. A realm with new code
355 // publishes a new name — treasury/spend/v2 — which the holders then have
356 // to adopt knowingly, which is the entire point.
357 if g.entryOf(name) != nil {
358 panic("govern: that name is taken; publish a new one rather than " +
359 "rewriting what may already be under a vote")
360 }
361 g.kinds.Set(name, &entry{kind: k, rules: r})
362
363 // The terms go out with it. A holder deciding whether to adopt this is
364 // deciding on the code AND on the bar it would pass at, and the two are
365 // bound together from here — Offer refuses to rewrite either.
366 chain.Emit(kindOfferedEvent,
367 "name", name,
368 "offerer", who.String(),
369 "quorumBps", strconv.FormatInt(r.QuorumBps, 10),
370 "thresholdBps", strconv.FormatInt(r.ThresholdBps, 10),
371 "votingBlocks", strconv.FormatInt(r.VotingBlocks, 10),
372 "delayBlocks", strconv.FormatInt(r.DelayBlocks, 10),
373 "graceBlocks", strconv.FormatInt(r.GraceBlocks, 10),
374 "proposeBps", strconv.FormatInt(r.ProposeBps, 10),
375 )
376}
377
378// maxTitle bounds the headline, which goes on the shared page. Every open
379// proposal's title is concatenated into Render(""), and the only cap there is
380// on the decided tail — so the page every holder reads is as long as the open
381// titles put together. Anything needing more room is what the payload is for.
382const maxTitle = 120
383
384// maxPayload bounds the argument. Looser than the title on purpose: this is
385// what the proposal actually says, a batch carries up to maxBatch members in
386// one, and the cost falls on the proposer — Render lists titles, so nobody
387// loads a payload without asking for that proposal by number.
388const maxPayload = 4096
389
390// maxKindName bounds a name. Offer is ungated, so it is chosen by whoever
391// turns up, and everything that renders a proposal renders it.
392const maxKindName = 64
393
394// mustBeUsableName refuses a name that cannot be used everywhere a name is
395// used.
396//
397// A kind's name is not only a registry key. It is the first field of a batch
398// member line, and parseBatch ends that field at the first SPACE; it goes into
399// the digest that decides whether two proposals are the same question; and it
400// is rendered on a page somebody has to read before voting. A name that works
401// in one of those and not the others is a kind that is quietly unusable.
402//
403// A space is the one that looks harmless and is not. "my kind" offers and
404// proposes perfectly well, and then a batch reads everything after the space
405// as the payload — so the member resolves to a kind called "my", the batch is
406// refused with "no kind called my", and nothing points at the name.
407//
408// Printable ASCII, no space. That is what every kind here already uses — fee,
409// treasury/spend, govern:adopt — and it is the largest set that works in all
410// three places at once.
411func mustBeUsableName(name string) {
412 if name == "" {
413 panic("govern: a kind needs a name")
414 }
415 if len(name) > maxKindName {
416 panic("govern: that name is too long")
417 }
418 for i := 0; i < len(name); i++ {
419 if c := name[i]; c <= ' ' || c > '~' {
420 panic("govern: a kind name must be printable ASCII with no spaces")
421 }
422 }
423}
424
425// mustBeUsableTitle bounds the one piece of free prose an ungated caller puts
426// on a page everybody reads.
427//
428// A title goes onto the front page inside a markdown list item, so a newline
429// ends that item and starts a row of the proposer's own — a forged proposal
430// with a state and a link they choose:
431//
432// - [#1](:1) **x
433// - [#999](:999) **Ratified by the foundation** — `govern:minter` · succeeded
434//
435// The bootstrap kinds ask for no stake, so that cost nothing but a transaction.
436//
437// Control characters, not printable-ASCII: a title is prose and keeps spaces
438// and any language. Markdown within a line is left alone — it cannot leave the
439// row it belongs to, and structure is the part a reader cannot check.
440func mustBeUsableTitle(title string) {
441 if len(title) > maxTitle {
442 panic("govern: that title is too long")
443 }
444 if err := TextOnly(title); err != nil {
445 panic("govern: a title " + err.Error())
446 }
447}
448
449// TextOnly reports whether a string is text rather than page structure.
450//
451// Exported because it is a kind author's problem. A payload comes from whoever
452// proposes — any address at all when ProposeBps is zero — and Describe renders
453// it onto a page others vote from, so a raw payload hands page structure to a
454// stranger. The built-ins are safe by accident: their payloads are kind names
455// and addresses, which cannot hold a newline. It bites the first kind that
456// takes free text.
457//
458// func (k mykind) Check(payload string) error {
459// if len(payload) > 280 {
460// return errTooLong
461// }
462// return govern.TextOnly(payload)
463// }
464//
465// Control characters, not printable-ASCII, so prose in any language survives —
466// every byte of a multi-byte character is >= 0x80. Markdown within a line is
467// allowed: it cannot leave the row it belongs to.
468//
469// The governor cannot apply this to payloads itself; govern:batch is defined
470// in terms of newlines. Only a kind knows its own grammar.
471func TextOnly(s string) error {
472 for i := 0; i < len(s); i++ {
473 if c := s[i]; c < 0x20 || c == 0x7f {
474 return errNotTextOnly
475 }
476 }
477 return nil
478}
479
480var errNotTextOnly = govErr("cannot contain control characters")
481
482// saneRules is the one definition of terms this realm will accept — one
483// function, two callers, after two copies drifted apart.
484//
485// Both copies missed ProposeBps. Above a hundred percent it asks a proposer to
486// hold more than everything, so nobody can open a question under that kind:
487// adopted and unusable. And govern:rules accepted only five of the six terms,
488// so the one field where a bad value bricks a kind was the one nobody could
489// retune. It takes all six now.
490func saneRules(r Rules) error {
491 if r.QuorumBps < 0 || r.QuorumBps > bps || r.ThresholdBps < 0 || r.ThresholdBps > bps {
492 return govErr("basis points out of range")
493 }
494 if r.ProposeBps < 0 || r.ProposeBps > bps {
495 // Exactly bps is allowed: "only somebody holding the entire supply may
496 // propose this" is a coherent, if severe, thing to want.
497 return govErr("a proposer cannot be asked to hold more than the whole supply")
498 }
499 if r.VotingBlocks <= 0 {
500 return govErr("a vote needs a period")
501 }
502 if r.GraceBlocks <= 0 {
503 // A decision has to be able to expire, because never expiring leaks a
504 // slot.
505 //
506 // A Succeeded proposal keeps its place in the open index until it is
507 // executed, because until then a duplicate would be a second live copy
508 // of the same question. With no grace period there is nothing that ever
509 // ends it: Cancel refuses a decided proposal, Settle and sweep find
510 // nothing to change, and the slot is held for the life of the realm.
511 // Sixty-four like that and nobody can open a proposal again.
512 //
513 // Refusing zero is what makes "every proposal eventually gives its slot
514 // back" true by construction, and it agrees with what this realm
515 // already says about grace: a decision nobody executed is a decision
516 // about a world that has moved.
517 return govErr("a decision has to expire; a grace period of zero holds " +
518 "its slot for the life of the realm")
519 }
520 if r.DelayBlocks < 0 {
521 // GraceBlocks < 0 needs no test here: the GraceBlocks <= 0 return above
522 // already refuses it. Negative behaves as zero everywhere it is used, so
523 // this refuses a value that means nothing rather than one that does harm —
524 // and stops it being stored and rendered as a term somebody agreed to.
525 return govErr("a period cannot be negative")
526 }
527 return nil
528}
529
530func (g *Governor) mustBeSaneRules(r Rules) {
531 if err := saneRules(r); err != nil {
532 panic("govern: " + err.Error())
533 }
534}
535
536// init makes the governor able to govern itself, and nothing else. These are
537// live from the first block because adopting a kind is itself a kind, so there
538// is nothing to bootstrap them with. Everything beyond must be offered and
539// adopted, so not even the deployer can add a power without a vote.
540
541// BootstrapRules are the terms the governor governs ITSELF on.
542//
543// Exported because a consuming realm registering a power of its own —
544// the mint, most of all — should govern it on the same terms rather than
545// inventing a second set that can drift from these.
546func BootstrapRules() Rules {
547 return Rules{
548 // A fifth of the supply has to turn out. Low enough to reach with
549 // dispersed holders, high enough that a handful cannot decide alone
550 // while everybody else is asleep.
551 QuorumBps: 2000,
552
553 // Two thirds of what was cast, not half. A simple majority is the
554 // wrong bar for changing the rules of the game — these kinds decide
555 // who may mint and what powers exist — and 6600 is exact where gno's
556 // own govdao carries 66.66 as a float64 and compares with >=.
557 ThresholdBps: 6600,
558
559 // A week to vote, so holders in any timezone get a turn. It is a
560 // DEADLINE and not a duration: a vote nothing further could change
561 // closes on the arithmetic, so an agreed decision does not wait it out.
562 VotingBlocks: 7 * 24 * 60 * 60 / 5,
563
564 // Two days between deciding and doing, which is what a timelock is
565 // for: time for somebody who dislikes the outcome to sell, leave, or
566 // argue before it takes effect.
567 DelayBlocks: 2 * 24 * 60 * 60 / 5,
568
569 // A fortnight to execute, and then the decision expires. A proposal
570 // nobody executed for two weeks is a decision about a world that has
571 // moved, and gno's own govdao had to bolt on a reject path precisely
572 // because proposals could otherwise sit forever.
573 GraceBlocks: 14 * 24 * 60 * 60 / 5,
574 }
575}
576
577// installBuiltins registers the kinds the governor governs itself with.
578func (g *Governor) installBuiltins() {
579 // The terms the governor governs ITSELF on. See BootstrapRules.
580 bootstrap := BootstrapRules()
581 g.kinds.Set(adoptKind{}.Name(), &entry{kind: adoptKind{}, rules: bootstrap, live: true})
582 g.kinds.Set(retireKind{}.Name(), &entry{kind: retireKind{}, rules: bootstrap, live: true})
583 g.kinds.Set(rulesKind{}.Name(), &entry{kind: rulesKind{}, rules: bootstrap, live: true})
584 g.kinds.Set(batchKind{}.Name(), &entry{kind: batchKind{}, rules: bootstrap, live: true})
585}
586
587// adoptKind turns an offered kind live. Its payload is only a name, so what
588// the holders read is exactly what they are approving.
589type adoptKind struct{}
590
591func (k adoptKind) Name() string { return reserved + "adopt" }
592
593func (k adoptKind) describe(g *Governor, payload string) string {
594 out := "adopt the kind `" + payload + "`, letting it be proposed and executed"
595 e := g.entryOf(payload)
596 if e == nil {
597 return out + "\n\n_Nothing has been offered under that name._"
598 }
599 // The terms, not just the name: a holder approving a power needs to see how
600 // easily it can be used. A hostile realm offers a spending kind with a
601 // hundredth of a percent quorum and a one-block vote, and the adoption
602 // reads exactly like an honest one.
603 //
604 // Still a total function of the payload — an offered name is bound to its
605 // code permanently, and govern:rules refuses to retune an unadopted kind,
606 // so none of this can move between proposing and executing.
607 out += ufmt.Sprintf("\n\nIt would pass on:"+
608 "\n- quorum: %s of the supply"+
609 "\n- threshold: %s of votes cast"+
610 "\n- time to vote: %s"+
611 "\n- delay before it can run: %s"+
612 "\n- expires after: %s",
613 pct(e.rules.QuorumBps), pct(e.rules.ThresholdBps),
614 span(e.rules.VotingBlocks), span(e.rules.DelayBlocks), span(e.rules.GraceBlocks))
615 // Always rendered, including when it is zero — which is when it matters
616 // most. Zero means Propose skips the stake check, so any address may open
617 // a proposal holding nothing, and omitting the line read as "not
618 // applicable" rather than "anybody". A permissive default is the term a
619 // page most needs to say out loud.
620 out += "\n- who may propose: " + proposerBar(e.rules.ProposeBps)
621 return out
622}
623
624func (k adoptKind) check(g *Governor, payload string) error {
625 e := g.entryOf(payload)
626 if e == nil {
627 return errNoSuchKind
628 }
629 if e.live {
630 return errAlreadyLive
631 }
632 return nil
633}
634
635func (k adoptKind) run(g *Governor, dispatch Dispatch, payload string) error {
636 e := g.entryOf(payload)
637 if e == nil {
638 return errNoSuchKind
639 }
640 e.live = true
641 return nil
642}
643
644// retireKind withdraws one. Offered-but-not-live is the resting state, so a
645// retired kind can be adopted again without the realm re-offering it.
646type retireKind struct{}
647
648func (k retireKind) Name() string { return reserved + "retire" }
649
650func (k retireKind) describe(g *Governor, payload string) string {
651 return "retire the kind `" + payload + "`, so it can no longer be proposed" +
652 "\n\nThis also kills any proposal of that kind that has already been " +
653 "decided and not yet run: Execute refuses a kind that is no longer " +
654 "adopted, and records the proposal as failed. Retiring is not only " +
655 "about the future."
656}
657
658func (k retireKind) check(g *Governor, payload string) error {
659 e := g.entryOf(payload)
660 if e == nil {
661 return errNoSuchKind
662 }
663 if !e.live {
664 return errNotLive
665 }
666 if isReserved(payload) {
667 return errCannotRetireBuiltin
668 }
669 return nil
670}
671
672func (k retireKind) run(g *Governor, dispatch Dispatch, payload string) error {
673 e := g.entryOf(payload)
674 if e == nil {
675 return errNoSuchKind
676 }
677 e.live = false
678 return nil
679}
680
681// Propose opens a question. One maketx call, two strings — which is the whole
682// design and the reason the payload is not a struct.
683//
684// The quorum is the rules' QuorumBps fraction of the engaged weight. A consumer
685// that computes its own absolute bar — a court sizing quorum to a claim's open
686// interest — uses ProposeWithQuorum instead.
687func (g *Governor) Propose(who address, kind, payload, title string) int64 {
688 return g.propose(who, kind, payload, title, 0)
689}
690
691// ProposeWithQuorum is Propose with an ABSOLUTE turnout the question needs,
692// replacing the rules' QuorumBps fraction for this one proposal. The figure is
693// the consumer's to compute (a court's max(5% supply, min(1×X̄, ⅓ votable))) and
694// is snapshotted here, so the bar cannot move under an open vote. A non-positive
695// floor is refused — a caller wanting the bps formula calls Propose.
696//
697// Additive: Propose is unchanged and passes zero, which every existing user does.
698func (g *Governor) ProposeWithQuorum(who address, kind, payload, title string, quorumFloor int64) int64 {
699 if quorumFloor <= 0 {
700 panic("govern: a quorum floor has to be positive; use Propose for the bps bar")
701 }
702 return g.propose(who, kind, payload, title, quorumFloor)
703}
704
705func (g *Governor) propose(who address, kind, payload, title string, quorumFloor int64) int64 {
706 if g.executing {
707 panic("govern: cannot propose from inside an execution or a vote")
708 }
709 // Held for the call, for the reason castVote's own note gives at length: this
710 // function reads the consumer-supplied Electorate four times (Epoch,
711 // PastTotal, EngagedTotal, PastVotes) and writes a proposal afterwards, so
712 // the read-only check above was excluding nothing on its own.
713 //
714 // Its exposure is milder than castVote's — there is no
715 // check-then-write-the-thing-you-checked pair here, so a nested frame would
716 // mint a second proposal rather than double-count a tally — but two
717 // proposals from one call is still a write the caller did not ask for, and
718 // the latch is the same two lines.
719 g.executing = true
720 defer func() { g.executing = false }()
721 // Checked before anything else is done with them: these are the two
722 // strings an ungated caller supplies, and they are persisted for the life
723 // of the realm.
724 mustBeUsableTitle(title)
725 if len(payload) > maxPayload {
726 panic("govern: that payload is too long")
727 }
728 k := g.kindOf(kind)
729 if err := g.checkKind(k, payload); err != nil {
730 panic("govern: " + err.Error())
731 }
732
733 // The electorate is the checkpointed supply at the last sealed epoch. Not a
734 // hand-kept roll: the token already remembers who held what, and asking it is
735 // what makes the weight historical.
736 //
737 // THIS ALONE DOES NOT MAKE WEIGHT UNRENTABLE, and an earlier version of this
738 // comment claimed it did. The anchor is derived HERE, at propose time, by
739 // whoever proposes — so a renter buys float, waits ONE epoch for the
740 // checkpoint to seal, proposes (pinning the anchor to a window in which they
741 // hold), sells, and votes with weight they no longer own. Measured end to end:
742 // 300 B of pinned weight cast at 600x a quorum floor with a live balance of
743 // zero and one epoch of capital at risk. What this line buys on its own is
744 // only that weight cannot be acquired AFTER the question exists.
745 //
746 // CLOSED, and not here — the other half is supplied by the CONSUMER, through
747 // VoteWithCap, as a ceiling of the voter's live balance. The engine still
748 // derives this snapshot and takes the lesser, so a consumer can only lower.
749 // See VOTEFLOOR.md; kourtv2 caps all three of its lanes and r/govern does not,
750 // which is why the exposure is described here rather than fixed here.
751 at := g.voters.Epoch() - 1
752 if at == 0 {
753 panic("govern: no sealed epoch yet — the chain is too young to vote")
754 }
755 total := g.voters.PastTotal(at)
756 if total <= 0 {
757 panic("govern: nothing was in issue at that epoch")
758 }
759 if total > maxWeighable {
760 // The ceiling the tally silently assumed. grc20votes caps here so it never
761 // trips; a swapped-in electorate that does not would overflow yes*bps into
762 // a negative tally — a won vote reported as lost. Refused at the door,
763 // where it reads as a misconfigured electorate rather than the governor
764 // miscounting.
765 panic("govern: the snapshot supply exceeds what a tally can weigh")
766 }
767 // Snapshotted with the rest, so a bar cannot move under an open vote.
768 engaged := g.voters.EngagedTotal(at)
769 if engaged <= 0 {
770 // An electorate that engages nobody makes every quorum TRIVIAL, which is
771 // the opposite of what this comment said for a long time and the reason
772 // the guard is load-bearing rather than tidy. The bar is
773 // `cast*bps >= engaged*QuorumBps`; at engaged == 0 the right-hand side is
774 // zero and the comparison holds for any turnout, including none — so
775 // quorum stops existing, and at the deadline one yes vote carries the
776 // question however few showed up.
777 //
778 // Reachable with an honest electorate rather than a broken one:
779 // EngagedTotal is there so a realm can drop idle weight out of the
780 // denominator, and one where all weight is idle returns zero truthfully.
781 panic("govern: the engaged weight has to be positive")
782 }
783 if engaged > total {
784 // Clamp rather than refuse. A court deliberately hosts claims too big for
785 // its electorate to decide, and an EngagedTotal above the snapshot supply
786 // should make the bar unreachable-but-valid, not abort the proposal.
787 // Clamping to total keeps rest = total - cast non-negative in the tally.
788 // The token this ships with returns engaged == total, so this never fires
789 // for it — it is here for a replacement electorate.
790 engaged = total
791 }
792
793 rules := g.rulesOf(kind)
794 if rules.ProposeBps > 0 {
795 // The numbers, not just the verdict. The realm knows the bar and what
796 // the caller held; withholding both leaves somebody to work out from
797 // the terms page what they are short of, and the epoch matters most of
798 // all — a holder refused here may well hold plenty NOW and nothing at
799 // the sealed epoch this is weighed at, which reads as a bug rather than
800 // as the anti-flash-loan rule doing its job.
801 if held := g.voters.PastVotes(who, at); held*bps < total*rules.ProposeBps {
802 panic(ufmt.Sprintf("govern: not enough voting power to open this: "+
803 "%s of the supply is required, and you held %d of %d at epoch %d",
804 pct(rules.ProposeBps), held, total, at))
805 }
806 }
807 // Everything may fill the board except the last few slots, which the
808 // governor's own kinds keep — see govLanes.
809 limit := maxLive
810 if !isReserved(kind) {
811 limit = maxOpen
812 }
813 if g.openIdx.Size() >= limit {
814 // Try to reclaim before refusing: a full list is usually full of
815 // finished business nobody has written down.
816 g.sweep()
817 }
818 if g.openIdx.Size() >= limit {
819 if limit < maxLive {
820 panic("govern: too many proposals are already open; the last " +
821 "few slots are kept for the governor's own kinds")
822 }
823 panic("govern: too many proposals are already open")
824 }
825 d := g.digest(kind, payload)
826 if v := g.openIdx.Get(d); v != nil {
827 // Settle the one already standing here before refusing on its account.
828 //
829 // The index holds finished proposals too — a read can work out that a
830 // proposal lost but cannot write it down, and the sweep above only
831 // runs when the list is FULL. So without this, a question defeated on
832 // its deadline and left unrecorded blocks the same question from being
833 // asked again, and the refusal says "already open" about something
834 // that closed.
835 //
836 // Targeted rather than a scan: the one proposal actually in the way,
837 // on the rare path where a digest collides at all.
838 if pv := g.proposals.Get(v.(string)); pv != nil {
839 g.settle(pv.(*proposal))
840 }
841 }
842 if g.openIdx.Has(d) {
843 panic("govern: an identical proposal is already open")
844 }
845 now := g.voters.Height()
846 g.propSeq++
847 p := &proposal{
848 id: g.propSeq, kind: kind, payload: payload, title: title, proposer: who,
849 rules: rules, epoch: at, total: total, engaged: engaged,
850 quorumFloor: quorumFloor,
851 opened: now, closes: now + rules.VotingBlocks,
852 openedTime: g.voters.Now(),
853 closesTime: g.voters.Now() + rules.VotingBlocks*secsPerBlock,
854 // The voter roll is bought here, by the proposer, including its first
855 // node — see rollSentinel just below the Set. Whoever asks, pays.
856 //
857 // The alternative is to let the first voter buy it, on the argument
858 // that a proposal nobody answers should cost nothing to leave
859 // unanswered. That saving only ever accrues to a proposal that failed,
860 // and it charges the first voter eleven times what the second pays.
861 // Being early is the wrong thing to tax.
862 //
863 // Fanout 32 on purpose. A narrower tree makes that first node cheaper
864 // (2,699 bytes at fanout 8 against 4,955) until the ninth voter, where
865 // the tree splits and that vote costs 5,476 — thirteen times what the
866 // eighth paid, for arriving in the wrong order. See
867 // docs/DESIGN.md.
868 state: stateActive, voted: bptree.NewBPTree32(),
869 }
870 p.voted.Set(rollSentinel, ballot(0))
871 g.proposals.Set(enc(uint64(g.propSeq)), p)
872 g.openIdx.Set(d, enc(uint64(g.propSeq)))
873
874 chain.Emit(proposalOpenedEvent,
875 "id", strconv.FormatInt(g.propSeq, 10),
876 "kind", kind,
877 "proposer", who.String(),
878 "epoch", strconv.FormatUint(uint64(at), 10),
879 )
880 return g.propSeq
881}
882
883// Vote records a choice, weighed at the proposal's snapshot.
884func (g *Governor) Vote(who address, id int64, choice string) {
885 g.castVote(who, id, choice, "", 0)
886}
887
888// VoteWithCap is Vote where the consumer supplies a CEILING on the weight, and
889// the engine still derives the weight itself and takes the lesser.
890//
891// A CEILING, NEVER A WEIGHT, and the distinction is the whole reason this method
892// is shaped like this. An earlier attempt added VoteWithWeight, which took the
893// figure to tally. That was a permissionless verdict flip.
894//
895// A supplied weight is not drawn from p.total, so `cast` could exceed it and
896// `rest := p.total - cast` went NEGATIVE. Written out, the two early arms are
897//
898// early-succeed yes*bps >= (total - abstain)*T
899// early-defeat (total - no - abstain)*bps < (total - abstain)*T
900//
901// because turnout is yes+no+abstain, so yes+no+rest is identically total-abstain.
902// The damage was therefore an inflated NUMERATOR against a snapshotted denominator:
903// `yes` could exceed anything the electorate held while (total - abstain) stayed
904// put, passing on support that did not exist. And a negative rest shrinks
905// (yes+rest), so the defeat arm could fire on a question still open.
906//
907// AN EARLIER VERSION OF THIS COMMENT SAID the arm "reduced to yes*bps >= (total -
908// abstain)*T, dropping `no` out of the test entirely". The identity above shows
909// that reduction is not a symptom of anything: it holds always, and `no` is not in
910// the early-succeed comparison and never was. The description was repeated in four
911// files before the algebra was checked, which is what
912// TestTheEarlyArmsIgnoreNoAndACapOnlyDelays now pins. Here the consumer can only
913// LOWER what this package read for itself, so
914//
915// Σ w ≤ Σ PastVotes(·, p.epoch) ≤ PastTotal(p.epoch) = p.total
916//
917// holds as an inequality rather than as a promise a caller has to keep. A hostile
918// consumer cannot raise `cast`, and the contract electorate.gno says this engine
919// "cannot check and cannot recover from" stays enforced where it lives.
920//
921// NO SENTINEL. `cap <= 0` is refused, and Vote is the uncapped path. Treating zero
922// as "uncapped" was tried in the plan for this change and reopened the exploit
923// verbatim: a renter who has sold everything HAS a floor of zero, so zero is not an
924// edge case, it is the attack's terminal state.
925//
926// Why a consumer would want this: kourtv2 caps at the voter's live balance, so
927// weight that has been sold back cannot vote. See VOTEFLOOR.md.
928func (g *Governor) VoteWithCap(who address, id int64, choice string, cap int64) {
929 if cap <= 0 {
930 panic("govern: a vote cap must be positive; use Vote for no cap")
931 }
932 g.castVote(who, id, choice, "", cap)
933}
934
935// VoteWithReason is Vote with a note the voter wants on the record — the same
936// pair OpenZeppelin has. Two entrypoints rather than one optional argument
937// because MsgCall carries a fixed list of strings and gno has no optional
938// parameters.
939//
940// The reason is emitted and never stored: it rides the Voted event, so the
941// voter pays gas for the bytes and the ledger carries none of them. Storing it
942// would be a new key per vote, on the path this design keeps cheapest.
943//
944// Bounded by maxReason. Not filtered through TextOnly, unlike a title: the
945// realm never renders this on a page, so a consumer that displays it is
946// responsible for its own escaping, as with any event field.
947func (g *Governor) VoteWithReason(who address, id int64, choice, reason string) {
948 if len(reason) > maxReason {
949 panic("govern: that reason is too long")
950 }
951 g.castVote(who, id, choice, reason, 0)
952}
953
954func (g *Governor) castVote(who address, id int64, choice, reason string, cap int64) {
955 if g.executing {
956 panic("govern: cannot vote from inside an execution or another vote")
957 }
958 // AND THE LATCH IS HELD FOR THIS CALL, not merely read at the top of it.
959 //
960 // It was read here and written only in Execute, which means it guarded
961 // nothing on this path: a check with no corresponding write cannot exclude
962 // anything. The hazard is specific and it is this function's own ordering —
963 // the double-vote guard below reads p.voted, and p.voted is not written until
964 // after g.voters.PastVotes has been called. Between those two lines sits a
965 // call into an interface the CONSUMER supplies.
966 //
967 // So an Electorate whose PastVotes re-enters Vote for the same voter and
968 // proposal gets a nested frame that passes the executing check (false) and
969 // passes the already-voted check (not yet written), recurses, and adds its
970 // weight again on every unwind. mustProposal hands back a pointer, so every
971 // frame is adding to the same tally.
972 //
973 // Holding the latch for the whole call closes it at the door rather than by
974 // reordering the body, which also covers g.settle's own reads of
975 // g.voters.Height/Now above and below — the same class of call, made twice
976 // more on this path.
977 //
978 // A consumer whose electorate is its own ledger — which is the only shape in
979 // this repo, kourtv2 passes its grc20votes ledger as both electorate and
980 // token — cannot reach this. The guard is for the ones that are not.
981 g.executing = true
982 defer func() { g.executing = false }()
983 p := g.mustProposal(id)
984 g.settle(p)
985 if p.state != stateActive {
986 panic("govern: that proposal is closed")
987 }
988 if p.voted.Has(string(who)) {
989 panic("govern: already voted")
990 }
991 // Weighed as of the snapshot, so buying in after the question was asked buys
992 // nothing, and selling out afterwards costs nothing.
993 //
994 // THE SECOND HALF IS A KNOWN, MEASURED EXPOSURE and it is still not closed
995 // HERE — it is closed by the caller, if the caller chooses to. VoteWithCap
996 // takes a ceiling and the clamp below applies it, so a consumer that passes
997 // the voter's live balance gets min(snapshot, held) and the rental dies;
998 // plain Vote leaves the exposure exactly as propose() describes it. kourtv2
999 // caps all three of its lanes; r/govern does not. VOTEFLOOR.md has the
1000 // derivation and why both halves are needed.
1001 //
1002 // A 29-line block describing a LIVE-BALANCE CAP stood here and was wrong twice
1003 // over: the cap was implemented, reverted in the same commit, and the prose
1004 // outlived it — in a commit whose subject was "correct a false safety claim".
1005 // It also argued against a design this file then shipped. Recorded because the
1006 // lesson is the expensive one: a comment describing a reverted mechanism is a
1007 // false safety claim with a plausible pedigree.
1008 w := g.voters.PastVotes(who, p.epoch)
1009 if w <= 0 {
1010 panic("govern: no voting power at that epoch")
1011 }
1012 // The consumer's ceiling, and it may only LOWER. Written as an explicit clamp
1013 // rather than a min() helper so that the one property this block exists for —
1014 // w is never raised — is a single readable line that a guard can pin.
1015 if cap > 0 && cap < w {
1016 w = cap
1017 }
1018 switch choice {
1019 case "yes":
1020 p.yes += w
1021 case "no":
1022 p.no += w
1023 case "abstain":
1024 // Turnout, not indifference. Showing up to decline is a different
1025 // statement from silence, and it counts towards quorum while staying
1026 // out of the threshold.
1027 p.abstain += w
1028 default:
1029 panic("govern: choice must be yes, no or abstain")
1030 }
1031 // For a long time nothing read this — only Has() was ever asked of the
1032 // tree — and it was kept anyway, on the measurement that swapping the whole
1033 // value for a bool saves ONE byte per vote, because what a vote costs is
1034 // the address key and the tree's own entry overhead rather than the value
1035 // hanging off it. VoteOf reads it now, which is what the byte was for.
1036 p.voted.Set(string(who), packBallot(choice, w))
1037 g.settle(p)
1038
1039 // The reason rides along only when there is one. An empty attribute on
1040 // every vote would cost every voter gas to say nothing, and would leave an
1041 // indexer unable to tell a voter who declined to explain from one whose
1042 // client cannot ask.
1043 if reason == "" {
1044 chain.Emit(votedEvent,
1045 "id", strconv.FormatInt(id, 10),
1046 "voter", who.String(),
1047 "choice", choice,
1048 "weight", strconv.FormatInt(w, 10),
1049 )
1050 return
1051 }
1052 chain.Emit(votedEvent,
1053 "id", strconv.FormatInt(id, 10),
1054 "voter", who.String(),
1055 "choice", choice,
1056 "weight", strconv.FormatInt(w, 10),
1057 "reason", reason,
1058 )
1059}
1060
1061// ballot is one vote as cast. The weight is what the tally used; the choice is
1062// here so a kind can pay the side that voted with it, which is the whole reason
1063// VoteOf exists — a scheme that pays only the winners gives a juror expecting to
1064// lose no reason to turn up, which suppresses the honest side during exactly
1065// the manipulation a vote is meant to stop.
1066// A ballot is one int64, not a struct.
1067//
1068// The tree hands out each value as its own object for lazy loading, and a
1069// struct there is a SECOND object per vote — measured at 846 bytes a vote
1070// against 454 for a scalar. A vote is the one thing in this realm that happens
1071// thousands of times, so 405 bytes of object header is the wrong place to spend
1072// on tidiness.
1073//
1074// weight*4 + the choice. Two bits for three choices, and the weight is bounded
1075// by maxSupply, which is MaxInt64/10000 — so the shift cannot overflow with
1076// four orders of magnitude to spare.
1077type ballot int64
1078
1079const (
1080 ballotYes int64 = iota
1081 ballotNo
1082 ballotAbstain
1083)
1084
1085func packBallot(choice string, weight int64) ballot {
1086 switch choice {
1087 case "yes":
1088 return ballot(weight<<2 | ballotYes)
1089 case "no":
1090 return ballot(weight<<2 | ballotNo)
1091 case "abstain":
1092 return ballot(weight<<2 | ballotAbstain)
1093 }
1094 // Unreachable: castVote refuses anything else before it gets here. Panics
1095 // rather than defaulting, because a silent default would file a vote under
1096 // a choice nobody made.
1097 panic("govern: not a choice: " + choice)
1098}
1099
1100func (b ballot) unpack() (choice string, weight int64) {
1101 switch int64(b) & 3 {
1102 case ballotYes:
1103 choice = "yes"
1104 case ballotNo:
1105 choice = "no"
1106 default:
1107 choice = "abstain"
1108 }
1109 return choice, int64(b) >> 2
1110}
1111
1112// rollSentinel is the key Propose writes into a new voter roll, so that the
1113// roll's first node is bought by the person asking the question rather than by
1114// whoever happens to answer it first.
1115//
1116// A bptree allocates nothing until its first key, and its leaf carries
1117// fanout-sized backing arrays, so that first key costs about eleven times an
1118// ordinary insert. Left to the first voter, that is a tax on being early —
1119// which is the worst moment to put one, since a proposal with no votes yet is
1120// the one that needs the first.
1121//
1122// The empty string, because it is the one key no address can be: an address is
1123// bech32 and bech32 is never empty. Readers taking an address from outside are
1124// still told to refuse it, since "" arrives as a valid Go string from anybody
1125// who wants to ask.
1126const rollSentinel = ""
1127
1128// VoteOf is how an address voted, for a kind that has to pay them.
1129//
1130// One address at a time, on purpose. A list of every voter is an unbounded
1131// return, and handing back the tree itself would be a live mutator holding this
1132// realm's authority — so payouts are pull-based: each claimant asks about
1133// themselves.
1134//
1135// Answers only while the roll survives. It is dropped by ReleaseRoll, so a kind
1136// that pays out should either do it inside Do, where the roll is certainly
1137// intact, or tell its claimants that the window closes when somebody reclaims
1138// the deposit.
1139func (g *Governor) VoteOf(id int64, who address) (choice string, weight int64, ok bool) {
1140 p := g.mustProposal(id)
1141 if p.voted == nil || who == rollSentinel {
1142 return "", 0, false
1143 }
1144 v := p.voted.Get(string(who))
1145 if v == nil {
1146 return "", 0, false
1147 }
1148 choice, weight = v.(ballot).unpack()
1149 return choice, weight, true
1150}
1151
1152// wouldBe is what the rules and the clock say a proposal's state is, without
1153// writing anything down.
1154//
1155// Split out from settle because reads must not mutate. Render walks every
1156// proposal, and a settle-on-read there is an unbounded write hiding inside a
1157// function that looks like a query — discarded harmlessly when it IS a query,
1158// and a surprise when some realm calls Render inside a transaction.
1159//
1160// Every multiplication here is safe because the supply is capped at maxSupply.
1161// yes, no and abstain are each bounded by the snapshot total, and total*bps
1162// fits in int64 by construction.
1163func (g *Governor) wouldBe(p *proposal, now int64) (int8, string) {
1164 switch p.state {
1165 case stateActive:
1166 cast := turnout(p)
1167 // The bar: an absolute floor the consumer set for this proposal, or the
1168 // rules' QuorumBps fraction of the engaged weight when it did not.
1169 var quorum bool
1170 if p.quorumFloor > 0 {
1171 quorum = cast >= p.quorumFloor
1172 } else {
1173 quorum = cast*bps >= p.engaged*p.rules.QuorumBps
1174 }
1175 // Every vote not yet cast, all of which could still say no. Negative if
1176 // an electorate's parts exceed its whole, which this token cannot do
1177 // and a replacement might; a clamp here changed no outcome, because a
1178 // broken roll settles on the first vote either way. The contract is on
1179 // the electorate instead, where somebody swapping one will read it.
1180 rest := p.total - cast
1181 // p.yes > 0 is not redundant, and leaving it out was a real hole.
1182 //
1183 // The threshold compares yes against yes+no. When everybody ABSTAINS
1184 // both are zero, the comparison is 0 >= 0, and the proposal passed —
1185 // immediately, with nobody in favour, on the strength of an empty
1186 // denominator. Abstain is documented as turnout without support and it
1187 // was carrying proposals.
1188 //
1189 // Nothing passes without somebody voting for it, whatever the
1190 // threshold is set to.
1191 if quorum && p.yes > 0 && p.yes*bps >= (p.yes+p.no+rest)*p.rules.ThresholdBps {
1192 // Decided even if every remaining vote goes against. Waiting out
1193 // the clock on a settled question is latency, not deliberation —
1194 // and it is only computable because the denominator was
1195 // snapshotted, which is a third reason to snapshot.
1196 return stateSucceeded, ""
1197 }
1198 // And the same argument the other way, which was missing.
1199 //
1200 // If the threshold cannot be reached even with every remaining vote in
1201 // favour, the question is as settled as one that has already won — and
1202 // this side is the more common one, because a proposal that is going
1203 // to lose usually loses by everybody ignoring it.
1204 //
1205 // It was asymmetric for no reason anybody could have defended: a
1206 // decided YES closed at once while a decided NO sat out its deadline,
1207 // holding a slot in a list bounded at maxLive, asking a question that
1208 // had already been answered.
1209 if p.yes+rest == 0 {
1210 // Nobody in favour and nobody left who could be. Settled, whatever
1211 // the clock says — this is the all-abstained case, which now loses
1212 // at once instead of sitting out its deadline having already
1213 // failed.
1214 return stateDefeated, "nobody voted in favour"
1215 }
1216 if (p.yes+rest)*bps < (p.yes+rest+p.no)*p.rules.ThresholdBps {
1217 return stateDefeated, "threshold can no longer be reached"
1218 }
1219 if g.votingClosed(p, now) {
1220 if quorum && p.yes > 0 && p.yes*bps >= forAndAgainst(p)*p.rules.ThresholdBps {
1221 return stateSucceeded, ""
1222 }
1223 if !quorum {
1224 return stateDefeated, "quorum not reached"
1225 }
1226 return stateDefeated, "threshold not reached"
1227 }
1228 return stateActive, p.reason
1229
1230 case stateSucceeded:
1231 if g.expired(p) {
1232 // A proposal nobody executed for a long time is a decision about a
1233 // world that has moved. gno's own govdao has no deadline at all
1234 // and had to bolt on a reject path because proposals could sit
1235 // forever.
1236 return stateExpired, "not executed within the grace period"
1237 }
1238 }
1239 return p.state, p.reason
1240}
1241
1242// settle writes down what wouldBe worked out. Only ever called from a
1243// transaction, because only a transaction can record anything.
1244func (g *Governor) settle(p *proposal) {
1245 now := g.voters.Height()
1246 next, reason := g.wouldBe(p, now)
1247 if next == p.state {
1248 return
1249 }
1250 g.setState(p, next, reason)
1251 if next == stateSucceeded {
1252 // The delay runs from when the outcome is WRITTEN DOWN, not from when
1253 // the votes stopped mattering. Those differ for a proposal that wins on
1254 // its deadline: nobody has to be present when a deadline passes, so the
1255 // outcome is only recorded by the next transaction.
1256 //
1257 // Right way round — a timelock exists so people can react, and there is
1258 // nothing to react to until the decision is announced. Settle is
1259 // permissionless so announcing is never a privileged position.
1260 p.ready = now + p.rules.DelayBlocks
1261 p.readyTime = g.voters.Now() + p.rules.DelayBlocks*secsPerBlock
1262 return
1263 }
1264}
1265
1266// Execute runs a passed proposal. Permissionless on purpose: after the vote
1267// and the delay there is nothing left to decide, so there is nobody left to
1268// trust with the decision.
1269// Dispatch runs an adopted kind. The consuming realm supplies it, because it is
1270// the only thing that can: minting the sub-realm token a kind is handed needs a
1271// live `cur`, and a /p/ package has none.
1272//
1273// It receives the kind, the sub-path to mint under, and the payload — and
1274// NOTHING ELSE. No pointer into governor or ledger state passes through here,
1275// which is what makes dispatching third-party code safe: gno-security-guide.md
1276// §3(C) is about a victim invoking a caller-supplied value while holding its
1277// own authority, and the damage in that class is done through a pointer
1278// parameter. There is none to give.
1279//
1280// The realm's implementation is one line:
1281//
1282// func(k governor.Kind, sub, payload string) error {
1283// return k.Do(0, cur.Sub(sub), payload)
1284// }
1285type Dispatch func(k Kind, subpath, payload string) error
1286
1287func (g *Governor) Execute(who address, id int64, run Dispatch) {
1288 if run == nil {
1289 // The engine holds no capability of its own, so without a dispatcher
1290 // there is nothing that could run a kind. Refused before anything is
1291 // written, so a caller who forgot one can supply it and retry.
1292 panic("govern: a dispatcher is required to run a kind")
1293 }
1294 if g.executing {
1295 panic("govern: re-entrant execution or vote")
1296 }
1297 p := g.mustProposal(id)
1298 g.settle(p)
1299 if p.state != stateSucceeded {
1300 panic("govern: that proposal is not waiting to be executed")
1301 }
1302 if g.inDelay(p) {
1303 // Says when, because "not yet" without a when is an invitation to poll.
1304 // The proposal page has carried this figure all along; the refusal a
1305 // caller actually receives did not.
1306 //
1307 // In the unit that GOVERNS: a caller told "1,000 blocks away" has to know
1308 // the chain's pace to turn that into "come back tomorrow", and the pace
1309 // is what this conversion exists to stop assuming. A proposal settled
1310 // before the stamps keeps the block form, which is all it ever had.
1311 if p.readyTime != 0 {
1312 panic(ufmt.Sprintf("govern: still in the delay window: executable at "+
1313 "%d (block %d), %d seconds away",
1314 p.readyTime, p.ready, p.readyTime-g.voters.Now()))
1315 }
1316 now := g.voters.Height()
1317 panic(ufmt.Sprintf("govern: still in the delay window: executable at "+
1318 "height %d, %d blocks away", p.ready, p.ready-now))
1319 }
1320 e := g.entryOf(p.kind)
1321 if e == nil || !e.live {
1322 // The holders withdrew this power while the proposal was waiting.
1323 // Recorded as a failure rather than a panic: the proposal is finished
1324 // either way, and a panic would leave it Succeeded forever, retried by
1325 // anyone, failing identically each time.
1326 g.setState(p, stateFailed, "the kind was retired before this could run")
1327 return
1328 }
1329 k := e.kind
1330
1331 // Checked again, against the world as it is now rather than as it was when
1332 // the question was asked.
1333 if err := g.checkKind(k, p.payload); err != nil {
1334 g.setState(p, stateFailed, "no longer valid: "+err.Error())
1335 return
1336 }
1337
1338 // Deferred, not cleared on the next line. If Do panics the transaction
1339 // aborts and the flag rolls back with everything else — but a recover
1340 // anywhere between here and there would leave the latch set and the
1341 // governor permanently unable to run anything again.
1342 g.executing = true
1343 defer func() { g.executing = false }()
1344 // The kind gets a SUB-REALM, never the realm's own capability — which is
1345 // why the sub-path is computed here and handed out, rather than left to
1346 // whoever writes the Dispatch.
1347 //
1348 // A live cur inside a foreign kind's Do has IsCurrent() true and the
1349 // consuming realm's PkgPath, so it is not "the realm as data", it is that
1350 // realm's authority, handed to code the holders adopted for one purpose.
1351 // With it a kind can cross() into any realm that trusts the consumer and be
1352 // seen as it, issue its tokens, and drain its banker. Adopting a treasury
1353 // kind would grant reach over every realm that names this one.
1354 //
1355 // cur.Sub gives a distinct pkgpath and a distinct address, and the VM
1356 // refuses RealmIssue for a sub-realm outright. A governed realm therefore
1357 // gates on the sub-path of the power it granted, which is also more useful
1358 // than gating on the governor as a whole.
1359 var err error
1360 if b, ok := asBuiltin(k); ok {
1361 // The governor's own kinds act on the governor. They are handed it
1362 // directly and never a sub-realm: there is no outside world for them
1363 // to reach, so there is nothing to grant.
1364 err = b.run(g, run, p.payload)
1365 } else {
1366 err = run(k, subPathOf(p.kind), p.payload)
1367 }
1368
1369 if err != nil {
1370 g.setState(p, stateFailed, err.Error())
1371 return
1372 }
1373 g.setState(p, stateExecuted, "")
1374}
1375
1376// ReleaseRoll drops a finished proposal's voter roll and refunds its deposit to
1377// whoever calls.
1378//
1379// The roll does not go when a proposal settles, because a kind that pays out to
1380// the people who voted has to ask after the fact, and pull-based claiming means
1381// the last claim can be a long way after execution. So it is reclaimed on
1382// request instead: nobody is obliged to, and whoever does is paid for it — the
1383// same bargain Settle makes for the slot.
1384//
1385// Refused twice over. While a proposal is open, because Vote reads the roll to
1386// refuse a second vote and dropping it early would let everybody vote twice.
1387// And while it has SUCCEEDED but not yet run, because that is exactly the
1388// window in which the kind has not read it yet — Execute is where a kind that
1389// pays its voters looks, so a stranger could otherwise empty the roll in the
1390// block before execution and leave the kind with nobody to pay. Waiting for the
1391// execution to land costs the reclaimer nothing; there is no deadline on this.
1392func (g *Governor) ReleaseRoll(who address, id int64) {
1393 p := g.mustProposal(id)
1394 g.settle(p)
1395 switch p.state {
1396 case stateActive:
1397 panic("govern: that proposal is still open; its roll is what refuses a second vote")
1398 case stateSucceeded:
1399 panic("govern: that proposal has not run yet; the roll is what it runs against")
1400 }
1401 p.voted = nil
1402}
1403
1404// Settle advances a proposal's state and frees its slot if it is finished.
1405//
1406// Needed because a read cannot persist: State and Render will tell you a
1407// proposal is defeated and tell you again tomorrow, since the transition they
1408// computed died with the query. Without a way to record it, the open list is a
1409// resource anybody can exhaust — fill every slot with proposals that will lose
1410// and nothing ever reclaims them.
1411//
1412// Permissionless, because it decides nothing: it writes down a conclusion the
1413// rules already reached.
1414func (g *Governor) Settle(who address, id int64) {
1415 g.settle(g.mustProposal(id))
1416}
1417
1418// sweep frees the slots of a few finished proposals. Bounded, and run when
1419// somebody proposes — the moment a full list matters, paid for by whoever wants
1420// the room. Unbounded here would make one unlucky proposer tidy the history.
1421//
1422
1423const sweepScan = 8
1424
1425func (g *Governor) sweep() {
1426 // openIdx cannot exceed maxLive — Propose refuses at the bound — so the
1427 // whole ring is a handful of windows. Trying them until one frees a slot is
1428 // still a BOUNDED scan; the bound that matters here is the index's, not the
1429 // history's, and it is the history that the per-window limit exists to keep
1430 // one unlucky proposer from paying to tidy.
1431 for w := 0; w <= maxLive/sweepScan; w++ {
1432 before := g.openIdx.Size()
1433 g.sweepWindow()
1434 if g.openIdx.Size() < before {
1435 return
1436 }
1437 }
1438}
1439
1440func (g *Governor) sweepWindow() {
1441 const scan = sweepScan
1442
1443 // Collected first, acted on after: settle removes from openIdx, and bptree
1444 // says a tree must not be modified during iteration (tree.gno). Calling it
1445 // from the callback removed the key the cursor stood on.
1446 //
1447 // No failure was observed and that is not evidence of safety — at this size
1448 // the tree tolerates it. The symptom, if it does not, is a slot that never
1449 // comes back, months later, under load.
1450 seen := 0
1451 last := ""
1452 var ids []string
1453 var stale []string
1454 g.openIdx.Iterate(g.sweepFrom, "", func(k string, v any) bool {
1455 seen++
1456 last = k
1457 if g.proposals.Get(v.(string)) == nil {
1458 stale = append(stale, k)
1459 } else {
1460 ids = append(ids, v.(string))
1461 }
1462 return seen >= scan
1463 })
1464 // Where to resume. A short window means the end of the index was reached,
1465 // so the next one starts over; otherwise it starts just past the last key
1466 // looked at. Appending a NUL gives the smallest string above a key, and the
1467 // keys are all the same length, so nothing can sort between the two.
1468 if seen < scan {
1469 g.sweepFrom = ""
1470 } else {
1471 g.sweepFrom = last + "\x00"
1472 }
1473
1474 for _, key := range ids {
1475 if p := g.proposals.Get(key); p != nil {
1476 g.settle(p.(*proposal))
1477 }
1478 }
1479 // Index entries whose proposal has gone entirely, which settle cannot
1480 // reach because there is nothing left to settle.
1481 for _, k := range stale {
1482 g.openIdx.Remove(k)
1483 }
1484}
1485
1486// Cancel withdraws a proposal. Only the proposer, only while it is still open.
1487func (g *Governor) Cancel(who address, id int64) {
1488 p := g.mustProposal(id)
1489 if who != p.proposer {
1490 panic("govern: only the proposer may cancel")
1491 }
1492 g.settle(p)
1493 if p.state != stateActive {
1494 panic("govern: too late to cancel")
1495 }
1496 // The one transition out of active that the rules do not make.
1497 g.setState(p, stateCanceled, "")
1498}
1499
1500// ----------------------------------------------------------------- reading --
1501
1502// State advances the clock before answering, so a reader never sees a proposal
1503// that is active only because nobody has poked it.
1504func (g *Governor) State(id int64) string {
1505 p := g.mustProposal(id)
1506 // The pure form: asking what a proposal's state is must not change it.
1507 st, _ := g.wouldBe(p, g.voters.Height())
1508 switch st {
1509 case stateActive:
1510 return "active"
1511 case stateDefeated:
1512 return "defeated"
1513 case stateSucceeded:
1514 return "succeeded"
1515 case stateExecuted:
1516 return "executed"
1517 case stateFailed:
1518 return "failed"
1519 case stateCanceled:
1520 return "canceled"
1521 default:
1522 return "expired"
1523 }
1524}
1525
1526// Preview renders what a payload would say, before anybody proposes it — so an
1527// interface can show somebody their proposal, and Check refuses a malformed one
1528// without spending a transaction.
1529//
1530// It also pins the thing the extension point rests on: govern calling INTO a
1531// kind in another realm. A stored interface value is re-resolved from the store
1532// on every call, so the method that runs is the offering realm's code.
1533//
1534// Reads whether or not the kind is adopted. What a payload would say is not a
1535// power, and refusing to preview an unadopted one makes the adoption vote
1536// harder to judge rather than safer.
1537func (g *Governor) Preview(kind, payload string) string {
1538 // The same bound Propose applies, at the cheaper door. Propose is a
1539 // transaction and this is a read, so an unbounded payload here is the
1540 // easier of the two to hand over — and it reaches the same Describe and
1541 // Check with it. Bounding one door and not the other is not bounding
1542 // anything.
1543 if len(payload) > maxPayload {
1544 return "that payload is too long"
1545 }
1546 e := g.entryOf(kind)
1547 if e == nil {
1548 return "no kind by that name has been offered"
1549 }
1550 out := g.describeKind(e.kind, payload)
1551 if err := g.checkKind(e.kind, payload); err != nil {
1552 out += "\n\n_This would be refused: " + err.Error() + "_"
1553 }
1554 if !e.live {
1555 out += "\n\n_This kind has not been adopted, so it cannot be proposed yet._"
1556 }
1557 return out
1558}
1559
1560// Describe is what the vote is about, rendered by the kind from the payload
1561// alone — the same string Do will be handed.
1562func (g *Governor) Describe(id int64) string {
1563 p := g.mustProposal(id)
1564 return g.describeOf(p)
1565}
1566
1567// HasVoted reports whether an address has already voted on a proposal.
1568//
1569// OpenZeppelin has hasVoted; without it the only way to find out is to send a
1570// vote and be refused, spending a transaction to learn what the realm knows. A
1571// wallet needs it before deciding whether to offer the buttons.
1572//
1573// Keeps answering after a proposal closes, because the roll survives until
1574// somebody calls ReleaseRoll. Once it has been released this answers false, so
1575// it is "is their vote still on record here", not "did they ever" — the
1576// permanent record of who voted is the Voted events.
1577func (g *Governor) HasVoted(id int64, who address) bool {
1578 p := g.mustProposal(id)
1579 return p.voted != nil && who != rollSentinel && p.voted.Has(string(who))
1580}
1581
1582// secsPerBlock converts a VotingBlocks rule — configured in blocks, as every
1583// governor rule is — into the wall-clock window it was chosen to mean.
1584const secsPerBlock = int64(5)
1585
1586// votingClosed reports whether a proposal has stopped taking votes.
1587//
1588// THE DEADLINE IS THE PROMISE, THE HEIGHT IS THE FALLBACK. Counted in blocks a
1589// window advertised as "four days" is four days only while the chain holds its
1590// assumed pace; the report that started this work showed a vote "closing in ~7
1591// days" beside an answer dated five years earlier. A proposal opened before the
1592// stamps existed has closesTime 0 and keeps the height it was opened under.
1593func (g *Governor) votingClosed(p *proposal, now int64) bool {
1594 if p.closesTime != 0 {
1595 return g.voters.Now() >= p.closesTime
1596 }
1597 return now >= p.closes
1598}
1599
1600// TimingsAt is Timings' wall-clock half: when the proposal opened and when
1601// voting closes, as unix seconds, or 0 for a proposal opened before the stamps.
1602//
1603// A SIBLING RATHER THAN A WIDER Timings, because Timings is consumed by more
1604// than one realm and arity is the kind of change that should arrive with its
1605// readers rather than ahead of them. Its first reader lands in the same commit
1606// as this method — kourtv2's ClaimTimeline — which is the rule the clock plan
1607// applies to constants and which holds just as well for a read.
1608//
1609// The heights stay on Timings and are still the reference; this is the number
1610// the gate actually compares against, so a page built from it cannot promise a
1611// close the governor will not honour.
1612func (g *Governor) TimingsAt(id int64) (openedTime, closesTime int64) {
1613 p := g.mustProposal(id)
1614 return p.openedTime, p.closesTime
1615}
1616
1617// Timings is a proposal's clock: when it opened, when voting closes, when it
1618// becomes executable, and when that chance lapses. The companion to Tally —
1619// without it, anything showing a countdown parsed the page, which is prose
1620// written for people.
1621//
1622// Two of these are zero rather than absent, and both zeroes mean something:
1623//
1624// ready == 0 the outcome is not recorded yet, so the timelock has not
1625// started. Settle starts it, and anybody may call it.
1626// expires == 0 it never expires, which is what a grace period of zero means
1627// everywhere else in this realm.
1628func (g *Governor) Timings(id int64) (opened, closes, ready, expires int64) {
1629 p := g.mustProposal(id)
1630 at, ever := expiresAt(p)
1631 if !ever {
1632 at = 0
1633 }
1634 return p.opened, p.closes, p.ready, at
1635}
1636
1637// Tally is the vote so far: yes, no, abstain, and the supply they are weighed
1638// against.
1639//
1640// The fourth number is the one worth having. Every bar here is a fraction —
1641// quorum of the snapshot supply, threshold of what was cast — so three counts
1642// without their denominator cannot be checked against anything. It is the
1643// supply at the proposal's snapshot epoch, not the supply now.
1644func (g *Governor) Tally(id int64) (yes, no, abstain, total int64) {
1645 p := g.mustProposal(id)
1646 return p.yes, p.no, p.abstain, p.total
1647}
1648
1649// Render is the governance page.
1650//
1651// It matters more here than a Render usually does. This design's whole claim
1652// over one that carries code is that what a voter reads IS what executes —
1653// Describe and Do consume the same string. That claim is only worth anything
1654// if somebody actually reads it, so the description is on the page rather than
1655// behind a call nobody makes.
1656func (g *Governor) render(path, notes string) string {
1657 if path != "" {
1658 return g.renderOne(path)
1659 }
1660 out := ufmt.Sprintf("# %s (%s)\n\n", g.token.Name(), g.token.Symbol())
1661
1662 // The token first, because the governance below is only meaningful in
1663 // terms of it: every quorum is a fraction of this supply and every vote is
1664 // weighed out of it.
1665 out += ufmt.Sprintf("- supply: %s %s (%d at %d decimals)\n",
1666 g.units(g.token.TotalSupply()), g.token.Symbol(), g.token.TotalSupply(), g.token.Decimals())
1667 out += notes
1668 out += ufmt.Sprintf("- epoch: %d (votes are weighed at sealed epochs)\n\n", g.voters.Epoch())
1669 out += "## governance\n\n"
1670
1671 var open, ready, done string
1672 nOpen := 0
1673 now := g.voters.Height()
1674
1675 // The open list IS openIdx: a proposal holds its slot until it reaches a
1676 // final state, so what is in there is exactly what belongs above the fold.
1677 // Bounded by maxLive rather than by how many questions have ever been
1678 // asked.
1679 g.openIdx.Iterate("", "", func(_ string, v any) bool {
1680 pv := g.proposals.Get(v.(string))
1681 if pv == nil {
1682 return false
1683 }
1684 p := pv.(*proposal)
1685 st, _ := g.wouldBe(p, now)
1686 if st != stateActive && st != stateSucceeded {
1687 // Finished, and still holding its slot because nobody has written
1688 // that down. The scan below reaches it as history, which is what
1689 // it is.
1690 return false
1691 }
1692 if st == stateSucceeded {
1693 ready += proposalLine(p, st)
1694 } else {
1695 open += proposalLine(p, st)
1696 }
1697 nOpen++
1698 return false
1699 })
1700
1701 // Newest first, and it stops as soon as the tail is full.
1702 //
1703 // The cap is on the WORK, not just on what gets appended. One pass over
1704 // every proposal the realm has ever held, computing each state and
1705 // truncating at the end, would grow forever while the page stayed the same
1706 // size — and this is the read every holder makes.
1707 g.proposals.ReverseIterate("", "", func(_ string, v any) bool {
1708 if len(done) >= 2000 {
1709 return true
1710 }
1711 p := v.(*proposal)
1712 st, _ := g.wouldBe(p, now)
1713 if st == stateActive || st == stateSucceeded {
1714 return false // already above, from g.openIdx
1715 }
1716 done += proposalLine(p, st)
1717 return false
1718 })
1719
1720 // Split by what the reader is asked to DO, not by which list the realm
1721 // keeps them in. Both hold a slot, but a decided proposal waiting out its
1722 // timelock is not something to vote on, and putting it under a heading that
1723 // means "still being voted on" tells the reader to do the wrong thing.
1724 //
1725 // The slot count belongs to the page rather than to either section. nOpen
1726 // counts everything holding a slot, decided-and-unrun included, so under a
1727 // voting heading it would sit above a shorter list — and vanish entirely
1728 // when every slot is held by decided proposals, which is when being full
1729 // matters most.
1730 // And the lane, once it is the only thing left. Below that point the count
1731 // is the whole story; at or above it, a holder reads free slots and is
1732 // refused anyway, which is the page contradicting the realm.
1733 out += ufmt.Sprintf("%d of %d slots in use", nOpen, maxLive)
1734 if nOpen >= maxOpen {
1735 out += ufmt.Sprintf("; the last %d are kept for the governor's own kinds, "+
1736 "so nothing else can be opened until one comes back", maxLive-maxOpen)
1737 }
1738 out += ".\n\n"
1739 if open == "" {
1740 out += "_Nothing to vote on._\n"
1741 } else {
1742 out += "### to vote on\n\n" + open
1743 }
1744 if ready != "" {
1745 out += "\n### decided, waiting to be run\n\n" + ready
1746 }
1747 if done != "" {
1748 out += "\n### finished\n\n" + done
1749 }
1750 return out
1751}
1752
1753func (g *Governor) renderOne(path string) string {
1754 id, err := strconv.ParseInt(path, 10, 64)
1755 if err != nil {
1756 return "# not a proposal id\n"
1757 }
1758 v := g.proposals.Get(enc(uint64(id)))
1759 if v == nil {
1760 return "# no such proposal\n"
1761 }
1762 p := v.(*proposal)
1763 st, reason := g.wouldBe(p, g.voters.Height())
1764
1765 out := ufmt.Sprintf("# #%d — %s\n\n", p.id, p.title)
1766 // The description first, and rendered by the kind from the payload alone.
1767 // This is the text the vote is about.
1768 out += g.describeOf(p) + "\n\n"
1769 out += ufmt.Sprintf("- kind: `%s`\n- proposer: %s\n- state: **%s**\n",
1770 p.kind, p.proposer.String(), stateName(st))
1771 if reason != "" {
1772 out += "- reason: " + reason + "\n"
1773 }
1774 // The snapshot supply, rendered as the header renders the live one — it is
1775 // the same quantity, and it was raw here and in units there.
1776 //
1777 // The tally BELOW stays raw on purpose. Its stated job is to let a reader
1778 // check the arithmetic rather than take the state on trust, and mixing
1779 // units into numbers somebody is about to multiply would take that away.
1780 // Two renderings of the same kind of figure, for two different jobs, on one
1781 // page — which is worth saying because it looks like the inconsistency it
1782 // was just fixed for.
1783 out += ufmt.Sprintf("- weighed at epoch %d, when %s %s was in issue (%d)\n",
1784 p.epoch, g.units(p.total), g.token.Symbol(), p.total)
1785 out += ufmt.Sprintf("\n## votes\n\n- yes %d\n- no %d\n- abstain %d\n",
1786 p.yes, p.no, p.abstain)
1787 // Both bars, as counted, so a reader can check the arithmetic rather than
1788 // take the state on trust.
1789 cast := turnout(p)
1790 // Percentages, not basis points. The realm counts in bps because integers
1791 // are exact where a float is an argument waiting to happen, and pct() exists
1792 // precisely so that choice does not reach the page — a holder asked to read
1793 // "needs 6600 bps" is being asked to do arithmetic to find out whether their
1794 // vote mattered.
1795 // Against ENGAGED, because that is what quorum divides by. Equal to total
1796 // for this realm; an electorate that drops idle weight would otherwise show
1797 // a bar nobody is being measured against.
1798 //
1799 // A consumer-set absolute floor is rendered as the count it is, not a
1800 // fraction of engaged — the tally compares cast against that number, so the
1801 // page must show it or it contradicts the state it is describing.
1802 if p.quorumFloor > 0 {
1803 out += ufmt.Sprintf("\nturnout %d of %d, needs %d · ", cast, p.engaged, p.quorumFloor)
1804 } else {
1805 out += ufmt.Sprintf("\nturnout %d of %d, needs %s · ", cast, p.engaged, pct(p.rules.QuorumBps))
1806 }
1807 // Three cases, not two. The threshold weighs yes against yes+no, so it has
1808 // nothing to weigh when every vote was an abstention — but that is not the
1809 // same as nobody voting, and collapsing them prints a line that contradicts
1810 // itself:
1811 //
1812 // turnout 1000 of 1000, needs 20.00% · no votes cast
1813 //
1814 // The whole supply voted. Saying so on the left and denying it on the right
1815 // is worst for the one case this realm treats most carefully: an
1816 // all-abstained proposal is the one that would otherwise pass with nobody
1817 // in favour, which is why the support bar exists at all.
1818 switch {
1819 case forAndAgainst(p) > 0:
1820 out += ufmt.Sprintf("yes is %d of %d cast, needs %s\n",
1821 p.yes, forAndAgainst(p), pct(p.rules.ThresholdBps))
1822 case p.abstain > 0:
1823 out += "every vote was an abstention, so the threshold has nothing to weigh\n"
1824 default:
1825 out += "no votes cast\n"
1826 }
1827
1828 // When voting closes. The block below was written for the succeeded state
1829 // on the reasoning that it is the one state where a reader has something to
1830 // do and no way to know when — which was right, and stopped one state
1831 // short: an active proposal is where they have the most to do.
1832 //
1833 // The subtraction is safe. wouldBe returns a terminal state once now has
1834 // reached p.closes, so a proposal reading active has not.
1835 if st == stateActive {
1836 // THE DATE, not a block count. "closes in 120,960 blocks" is a promise
1837 // only a reader who knows the chain's pace can check, and the pace is not
1838 // a promise — that mismatch is what put a vote "closing in ~7 days" beside
1839 // an answer dated five years earlier. A proposal opened before the stamps
1840 // has no date and keeps the block form it was opened under.
1841 if p.closesTime != 0 {
1842 // A COUNTDOWN AND AN ABSOLUTE, because they answer different
1843 // questions: the count tells a reader how long they have, and the
1844 // date is what the gate will actually compare against, so a page
1845 // read at one moment is still checkable at the next. The old line
1846 // gave a count in BLOCKS, which is only a duration to a reader who
1847 // knows the chain's pace — and the pace is what was wrong.
1848 out += ufmt.Sprintf("\n_Voting closes in %d seconds, at %d (block %d)._\n",
1849 p.closesTime-g.voters.Now(), p.closesTime, p.closes)
1850 } else {
1851 out += ufmt.Sprintf("\n_Voting closes in %d blocks, at height %d._\n",
1852 p.closes-g.voters.Height(), p.closes)
1853 }
1854 }
1855
1856 // What happens next, for a decision that has been taken and not yet done.
1857 //
1858 // Without this the page says "succeeded" and stops, which is the one state
1859 // where a reader has something to DO and no way to know when. It is also
1860 // the state that looks most like nothing is happening: the proposal sits on
1861 // the open list, apparently unfinished, while a timelock it cannot see runs
1862 // down.
1863 if st == stateSucceeded {
1864 now := g.voters.Height()
1865 expiry, expires := expiresAt(p)
1866 switch {
1867 case p.state != stateSucceeded:
1868 // Decided and not yet written down — what a proposal winning on its
1869 // DEADLINE looks like, since nobody has to be present when one
1870 // passes.
1871 //
1872 // p.ready is zero until then, so the lines below would be
1873 // arithmetic against nothing — "succeeded" and "expired" printed
1874 // together. Saying what is actually needed is the useful thing
1875 // anyway, because anybody may Settle it.
1876 out += "\n_Decided, and nobody has recorded it yet. The delay " +
1877 "starts when somebody calls Settle — anybody may._\n"
1878 case g.inDelay(p):
1879 // SECONDS AND THE ABSOLUTE, for the reason the voting close carries
1880 // both: a count in blocks is a duration only to a reader who knows
1881 // the chain's pace. A proposal settled before the stamps keeps the
1882 // block form, which is all it ever had.
1883 if p.readyTime != 0 {
1884 out += ufmt.Sprintf("\n_Waiting: executable in %d seconds, at %d (block %d)._\n",
1885 p.readyTime-g.voters.Now(), p.readyTime, p.ready)
1886 } else {
1887 out += ufmt.Sprintf("\n_Waiting: executable in %d blocks, at height %d._\n",
1888 p.ready-now, p.ready)
1889 }
1890 case g.expired(p):
1891 out += "\n_Expired: nobody executed it in time._\n"
1892 case expires:
1893 out += ufmt.Sprintf("\n**Executable now**, until height %d.\n", expiry)
1894 default:
1895 out += "\n**Executable now.**\n"
1896 }
1897 }
1898 return out
1899}
1900
1901// proposerBar renders the stake a proposer must hold, where zero is not "none
1902// required" so much as "no check at all".
1903func proposerBar(bps int64) string {
1904 if bps <= 0 {
1905 return "anybody, holding nothing"
1906 }
1907 return pct(bps) + " of the supply"
1908}
1909
1910// turnout is every vote cast, abstentions included. One definition because it
1911// was two: the tally computed it and the page computed it again, so the quorum
1912// a proposal was DECIDED by and the figure a holder READ could diverge. That
1913// line has already produced two page-versus-state contradictions.
1914//
1915// Abstain counts here because it counts towards quorum, which is why the
1916// support bar exists separately.
1917func turnout(p *proposal) int64 { return p.yes + p.no + p.abstain }
1918
1919// forAndAgainst is what the threshold weighs: the votes that took a side.
1920//
1921// Abstentions are deliberately absent — they count towards quorum and not
1922// towards the bar — and that rule was written once in the tally and again on
1923// the page, which is how a page comes to disagree with the state it describes.
1924func forAndAgainst(p *proposal) int64 { return p.yes + p.no }
1925
1926// inDelay is whether a decided proposal is still inside its timelock.
1927//
1928// Execute refuses while this holds and the page counts down while it holds, and
1929// those two must not disagree by even a block: a page offering to run what
1930// Execute will refuse is the same defect as a page calling a live proposal
1931// expired. One definition, so a boundary cannot be moved on one side only.
1932func (g *Governor) inDelay(p *proposal) bool {
1933 if p.readyTime != 0 {
1934 return g.voters.Now() < p.readyTime
1935 }
1936 return g.voters.Height() < p.ready
1937}
1938
1939// expired reports whether a decided proposal is past its execution window.
1940//
1941// THE TWO WINDOWS MOVE TOGETHER, and that is why they convert together: the
1942// delay exists so people can react, and the grace exists so a decision about a
1943// world that has moved cannot be executed. Both are measured from p.ready, so
1944// converting one and not the other would let a proposal leave its delay on one
1945// clock and expire on the other — a gap where it is neither waiting nor
1946// executable, or an overlap where it is both.
1947func (g *Governor) expired(p *proposal) bool {
1948 if at, ever := expiresAtTime(p); ever {
1949 return g.voters.Now() > at
1950 }
1951 at, ever := expiresAt(p)
1952 return ever && g.voters.Height() > at
1953}
1954
1955// expiresAtTime is expiresAt in wall-clock seconds, or (0, false) for a
1956// proposal settled before the stamps existed.
1957func expiresAtTime(p *proposal) (int64, bool) {
1958 if p.rules.GraceBlocks <= 0 || p.readyTime == 0 {
1959 return 0, false
1960 }
1961 return p.readyTime + p.rules.GraceBlocks*secsPerBlock, true
1962}
1963
1964// expiresAt is when a decided proposal stops being executable, and whether it
1965// ever does. saneRules refuses a grace of zero, so in practice it always does.
1966//
1967// One definition because it is easy to write three: the tally testing
1968// `now > ready+grace`, the page testing it again, and the page printing the sum
1969// a third time as the height it offers to run until. Three independent copies
1970// of one deadline is how a page comes to print "succeeded" and "expired"
1971// together.
1972func expiresAt(p *proposal) (int64, bool) {
1973 // Nothing expires before the outcome is written down, because the window is
1974 // measured from p.ready and p.ready is zero until Settle sets it. Adding a
1975 // grace period to nothing gives a deadline in the realm's first hour, which
1976 // is how a page once printed "succeeded" and "expired" together.
1977 //
1978 // The page guards that by checking the recorded state before it asks; the
1979 // guard belongs here, where the arithmetic is. Timings walked straight into
1980 // it the moment it exposed this to a caller who had not read the page's
1981 // comment.
1982 // saneRules refuses a grace of zero, so that arm is defence rather than
1983 // policy; p.ready is the live one.
1984 if p.rules.GraceBlocks <= 0 || p.ready == 0 {
1985 return 0, false
1986 }
1987 return p.ready + p.rules.GraceBlocks, true
1988}
1989
1990// span renders a period in blocks, with roughly how long that is.
1991//
1992// Correctness never depends on the wall clock, but "120960 blocks to vote"
1993// does not tell a reader whether that is an afternoon or a season. Both are
1994// printed and the approximation is marked as one.
1995//
1996// Five seconds a block, the same assumption the bootstrap rules were chosen
1997// under. At another cadence the block counts are still exact and the hours are
1998// not, which is why they are hedged.
1999func span(blocks int64) string {
2000 if blocks <= 0 {
2001 return "no period"
2002 }
2003 secs := blocks * 5
2004 switch {
2005 case secs < 60*60:
2006 return ufmt.Sprintf("%d blocks (about %s)", blocks, plural(secs/60, "minute"))
2007 case secs < 48*60*60:
2008 return ufmt.Sprintf("%d blocks (about %s)", blocks, plural(secs/3600, "hour"))
2009 default:
2010 return ufmt.Sprintf("%d blocks (about %s)", blocks, plural(secs/86400, "day"))
2011 }
2012}
2013
2014// plural writes a count with its unit, in the number the count deserves.
2015//
2016// "1 hours" reads as something a machine wrote, and a page that reads as
2017// machine-written gets skimmed — which is a poor outcome for the text somebody
2018// is meant to study before granting a power.
2019func plural(n int64, unit string) string {
2020 if n == 1 {
2021 return "1 " + unit
2022 }
2023 return ufmt.Sprintf("%d %ss", n, unit)
2024}
2025
2026// units renders a base-unit amount the way a holder counts it. The ledger is
2027// integers all the way down, which is exact and unreadable: "supply: 1000"
2028// beside "decimals: 6" asks somebody to divide before they know whether they
2029// hold a millionth of the token or all of it. Both are printed — this for
2030// reading, the raw figure for checking.
2031//
2032// scale is 10^decimals for the token being rendered — computed, not a constant,
2033// because Go has no constant exponentiation and the engine does not know what token
2034// it renders until given one (TestTheEngineRendersTheTokensOwnScale checks it).
2035// Decimals is CLAMPED to 18 first: 10^19 overflows int64 (and 10^k for k≥64 wraps to
2036// 0, which would divide-by-zero in units()), so an unclamped power on a pathological
2037// token would corrupt or panic Render. Render is a read, so a clamp on the rare bad
2038// token is the right trade; grc20votes reports a small fixed decimals and is unaffected.
2039func (g *Governor) scale() int64 {
2040 d := g.token.Decimals()
2041 if d > 18 {
2042 d = 18
2043 }
2044 out := int64(1)
2045 for i := 0; i < d; i++ {
2046 out *= 10
2047 }
2048 return out
2049}
2050
2051func (g *Governor) units(n int64) string {
2052 whole := n / g.scale()
2053 frac := n % g.scale()
2054 // The sign has to be taken from n, not from the whole part. Integer
2055 // division truncates towards zero, so everything between -1 and 0 has a
2056 // whole part of exactly 0 — and a "-0" that FormatInt prints as "0". Taking
2057 // the minus from whole therefore dropped it for that entire range, and
2058 // -0.5 rendered as 0.5: not a near miss but the opposite number.
2059 sign := ""
2060 if n < 0 {
2061 sign = "-"
2062 whole, frac = -whole, -frac
2063 }
2064 out := strconv.FormatInt(frac, 10)
2065 for len(out) < g.token.Decimals() {
2066 out = "0" + out
2067 }
2068 // Trailing zeroes are noise; a whole number should read as one.
2069 for len(out) > 1 && out[len(out)-1] == '0' {
2070 out = out[:len(out)-1]
2071 }
2072 if out == "0" {
2073 return sign + strconv.FormatInt(whole, 10)
2074 }
2075 return sign + strconv.FormatInt(whole, 10) + "." + out
2076}
2077
2078// proposalLine is one row of the governance list.
2079func proposalLine(p *proposal, st int8) string {
2080 return ufmt.Sprintf("- [#%d](:%d) **%s** — `%s` · %s\n",
2081 p.id, p.id, p.title, p.kind, stateName(st))
2082}
2083
2084// maxReason bounds what a kind may write into the permanent record.
2085//
2086// An adopted kind is trusted — the holders voted it the power to run — so this
2087// is not a defence against a hostile one, which could do far worse than store a
2088// long string. It is a defence against a buggy one: an error carrying a whole
2089// response body or a stack of context lands in a proposal that is kept forever
2090// and rendered every time somebody opens it.
2091const maxReason = 256
2092
2093// clip truncates at a rune boundary, so a cut message stays valid UTF-8 rather
2094// than ending in half a character.
2095func clip(s string) string {
2096 if len(s) <= maxReason {
2097 return s
2098 }
2099 // Walk back to the first byte that is not a continuation byte. Cutting
2100 // BEFORE that byte is already a whole-rune prefix — the character it
2101 // begins is simply left out.
2102 //
2103 // Backing off one further, to drop the lead byte as well, is wrong and
2104 // looked right: it takes the cut inside the character before, which is how
2105 // this was first written. The test that caught it decodes what survives
2106 // rather than measuring it.
2107 n := maxReason
2108 for n > 0 && s[n]&0xC0 == 0x80 {
2109 n--
2110 }
2111 return s[:n] + "…"
2112}
2113
2114// proposalSettledEvent is the single announcement of an outcome.
2115//
2116// One event with a state on it rather than one event per outcome, because the
2117// per-outcome shape is what let four of the six transitions go unannounced:
2118// Executed and Failed were emitted at two of Execute's paths, its other
2119// failure path and the retired-kind path said nothing, and a proposal that
2120// simply LOST — the ordinary result — was never announced anywhere. It opened,
2121// it collected votes, and then nothing was ever said about it again.
2122const (
2123 // kindOfferedEvent is the only announcement a kind gets before it is
2124 // adopted, and the only one at all if it never is.
2125 //
2126 // The registry is never iterated — an ungated one is unbounded, and walking
2127 // it would put somebody's spam on the page holders read — so without this
2128 // an offered kind could be found only by guessing its name, and the
2129 // extension point was undiscoverable.
2130 //
2131 // Adoption gets no event: it happens through a proposal, which is already
2132 // announced when it opens and when it settles.
2133 kindOfferedEvent = "KindOffered"
2134
2135 proposalOpenedEvent = "ProposalOpened"
2136 votedEvent = "Voted"
2137 proposalSettledEvent = "ProposalSettled"
2138)
2139
2140// setState is the only writer of p.state, and does everything that follows from
2141// a proposal reaching one, so no new code path has to remember. Called only
2142// with a state out of stateActive: nothing transitions back in.
2143//
2144// Three things:
2145//
2146// - the reason is clipped, so a kind cannot write an unbounded string into a
2147// record kept for the life of the realm;
2148// - the slot goes, unless the proposal SUCCEEDED and is pending execution;
2149// - the outcome is announced.
2150//
2151// Not the voter roll, which outlives the decision. A kind that pays the people
2152// who voted has to ask after the proposal has finished, so the roll goes on
2153// request instead, at ReleaseRoll — permissionless, with the deposit going to
2154// whoever calls it, which is the same bargain Settle makes for the slot.
2155func (g *Governor) setState(p *proposal, st int8, reason string) {
2156 p.state, p.reason = st, clip(reason)
2157 if st != stateSucceeded {
2158 g.release(p)
2159 }
2160 chain.Emit(proposalSettledEvent,
2161 "id", strconv.FormatInt(p.id, 10),
2162 "state", stateName(st),
2163 "reason", p.reason,
2164 )
2165}
2166
2167func stateName(s int8) string {
2168 switch s {
2169 case stateActive:
2170 return "active"
2171 case stateDefeated:
2172 return "defeated"
2173 case stateSucceeded:
2174 return "succeeded"
2175 case stateExecuted:
2176 return "executed"
2177 case stateFailed:
2178 return "failed"
2179 case stateCanceled:
2180 return "canceled"
2181 }
2182 return "expired"
2183}
2184
2185func (g *Governor) entryOf(name string) *entry {
2186 if v := g.kinds.Get(name); v != nil {
2187 return v.(*entry)
2188 }
2189 return nil
2190}
2191
2192// subPathOf maps a kind name onto a legal sub-realm path.
2193//
2194// Sub takes '/'-separated segments of [a-z0-9] with '_.-' inside a segment, and
2195// a kind name is any printable ASCII without spaces — "govern:adopt" among
2196// them. Anything outside the alphabet becomes '-', which keeps the mapping
2197// total, stable and readable: a governed realm gating on this sees the power it
2198// granted rather than the governor as a whole.
2199func subPathOf(kind string) string {
2200 out := []byte(kind)
2201 for i := 0; i < len(out); i++ {
2202 c := out[i]
2203 switch {
2204 case c >= 'a' && c <= 'z', c >= '0' && c <= '9':
2205 case c >= 'A' && c <= 'Z':
2206 out[i] = c + ('a' - 'A')
2207 default:
2208 out[i] = '-'
2209 }
2210 }
2211 return string(out)
2212}
2213
2214// liveEntry is what a proposal needs: a kind the holders have adopted.
2215//
2216// The two failures are told apart because the remedy differs. A name nobody
2217// offered is a typo; a name that IS offered and not adopted is waiting on a
2218// vote, and "no such kind" points that reader at their spelling when what they
2219// need is govern:adopt. Preview has always distinguished them.
2220func (g *Governor) liveEntry(name string) *entry {
2221 e := g.entryOf(name)
2222 if e == nil {
2223 panic("govern: no such kind: " + name)
2224 }
2225 if !e.live {
2226 panic("govern: the kind " + name + " has been offered but not adopted, " +
2227 "so it cannot be proposed yet — the holders adopt it with govern:adopt")
2228 }
2229 return e
2230}
2231
2232func (g *Governor) kindOf(name string) Kind { return g.liveEntry(name).kind }
2233
2234// anyKind is the kind whether or not it is still adopted.
2235//
2236// Reading is not doing. Retiring a kind withdraws the power to run it; it must
2237// not also erase the record of what was already proposed under it — and it did,
2238// because everything that renders a proposal went through the live lookup and
2239// panicked. One retirement would have taken every historical page with it.
2240func (g *Governor) anyKind(name string) Kind {
2241 e := g.entryOf(name)
2242 if e == nil {
2243 return nil
2244 }
2245 return e.kind
2246}
2247
2248// describeOf renders a proposal, surviving a kind that has since been retired
2249// or was never offered here at all.
2250func (g *Governor) describeOf(p *proposal) string {
2251 k := g.anyKind(p.kind)
2252 if k == nil {
2253 return "_the kind `" + p.kind + "` is no longer registered, so this " +
2254 "proposal can no longer describe itself. Its payload was:_\n\n" +
2255 g.indented(p.payload)
2256 }
2257 return g.describeKind(k, p.payload)
2258}
2259
2260// indented puts EVERY line of a payload inside the code block. Four spaces in
2261// front of the whole string indents line one and leaves the rest as live
2262// markdown, and payloads may contain newlines — govern:batch is defined in
2263// terms of them.
2264//
2265// Reached only when a kind has been retired or was never registered, which is
2266// the one place a payload is rendered with nothing left to have vetted it: the
2267// validation belonged to that kind's Check.
2268func (g *Governor) indented(s string) string {
2269 return " " + strings.ReplaceAll(s, "\n", "\n ")
2270}
2271
2272func (g *Governor) rulesOf(name string) Rules { return g.liveEntry(name).rules }
2273
2274func (g *Governor) mustProposal(id int64) *proposal {
2275 v := g.proposals.Get(enc(uint64(id)))
2276 if v == nil {
2277 panic("govern: no such proposal")
2278 }
2279 return v.(*proposal)
2280}
2281
2282// Errors a kind returns. Values rather than strings built at the call site, so
2283// a caller can compare them and Describe cannot drift from Check.
2284
2285type govError struct{ s string }
2286
2287func (e *govError) Error() string { return e.s }
2288func govErr(s string) error { return &govError{s} }
2289
2290// digest identifies a question by what it asks, not by who asked it.
2291//
2292// Only ever used to refuse a duplicate while one is open. Identity stays a
2293// sequence number, because a number renders, sorts and paginates and a hash
2294// does none of those — OpenZeppelin derives the whole proposal id from the
2295// hash and pays for it every time a UI wants a list in order.
2296func (g *Governor) digest(kind, payload string) string {
2297 h := sha256.Sum256([]byte(kind + "\x00" + payload))
2298 return string(h[:16])
2299}
2300
2301// release frees a proposal's slot in the open index. Idempotent, because
2302// settle can reach a terminal state from more than one path.
2303func (g *Governor) release(p *proposal) { g.openIdx.Remove(g.digest(p.kind, p.payload)) }