gno.land/p/g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6/governor/v0
PackageOpen in gnoweb ↗- Kind
- Pure package
- Name
- v0
- Namespace
- g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6 / governor
- Exported functions
- n/a — not supported for pure packages by the node (vm/qfuncs)
- Module
- gno.land/p/g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6/governor/v0
- gno
- 0.9
electorate.gnogno
1package governor23import (4 bptree "gno.land/p/nt/bptree/v0"5)67// Electorate is where voting weight comes from, and the only thing tying this8
not supported for pure packages by the node (vm/qfuncs)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
// engine to any particular token. Everything else here is about proposals, so
9// with this extracted the engine can be driven by anything that can answer
10// four questions.
11//
12// It also makes the governor testable without a ledger. Reaching exactly a
13// quorum with real balances means solving for the supply, and a test that does
14// arithmetic to test arithmetic proves nothing.
15//
16// EVERY QUESTION HERE IS ABOUT A SEALED PAST EPOCH, which is what keeps the
17// Token/Electorate split below honest — Electorate about then, Token about now.
18// A live per-address read (VotesOf) was added here while fixing rented weight and
19// removed again when the fix was reverted; RENTEDWEIGHT.md records why. Anything
20// re-adding it is re-opening that decision, not making a small change.
21//
22// AND THE FIX THAT SHIPPED DID NOT NEED IT, which is the point worth keeping. The
23// live figure arrives as a CAP from the consumer (VoteWithCap) instead of as an
24// interface method the engine calls, so this interface stays entirely about THEN.
25// The engine cannot read a live balance even if it wanted to, and a supplied
26// ceiling can only lower what the engine derived — so coherence is structural
27// rather than something each implementor has to get right.
28type Electorate interface {
29 // PastVotes is what an address could vote at a sealed epoch.
30 PastVotes(who address, at uint32) int64
31
32 // PastTotal is everything that existed then, which quorum is a fraction
33 // of. Taken at the same epoch as the votes it is compared against, or a
34 // quorum could be met against a supply that no longer exists.
35 //
36 // It must be at least the sum of every PastVotes at that epoch. The
37 // governor subtracts what has been cast from this to decide whether a
38 // question is already settled, so a roll whose parts exceed its whole
39 // reports votes that cannot exist and settles early. Measured, not feared:
40 // one voter reporting a hundred against a claimed total of ten carried a
41 // proposal at a 99% threshold.
42 //
43 // This realm's token satisfies it exactly, and a test says so after every
44 // operation that moves power. Anything swapped in here needs its own. The
45 // governor cannot check it and cannot recover from it — a clamp was tried.
46 //
47 // It must also stay at or below maxWeighable (MaxInt64/bps): the tally
48 // computes yes*bps, and a supply above that overflows int64 into a negative
49 // tally — a won vote reported as lost. grc20votes caps its supply there
50 // exactly; the governor refuses a proposal whose snapshot supply exceeds it,
51 // so a swapped-in electorate that ignores the ceiling fails loudly at the door.
52 PastTotal(at uint32) int64
53
54 // EngagedTotal is the part of PastTotal that quorum is measured against.
55 //
56 // It exists so a realm can drop idle weight out of the denominator without
57 // touching anything in the governor: a fixed low bar is unreachable at
58 // launch and trivial later, and moving the denominator is the fix that
59 // keeps the bar meaningful at both ends.
60 //
61 // Should be <= PastTotal at the same epoch. Weight outside it can still vote
62 // — it simply is not counted in the bar — so the governor keeps using
63 // PastTotal for how many votes could still be cast. If it exceeds PastTotal
64 // the governor clamps it down to PastTotal, so an electorate that over-reports
65 // engagement makes the bar unreachable-but-valid rather than aborting.
66 //
67 // This realm's token returns PastTotal unchanged: every holder is engaged
68 // by definition, since power is self-delegated by default.
69 EngagedTotal(at uint32) int64
70
71 // Epoch is now. A proposal snapshots Epoch()-1 and stores it; nothing
72 // re-derives it later.
73 Epoch() uint32
74
75 // Height is the block height the governor gates on: when voting opens and
76 // closes, when a delay has elapsed, when a proposal may execute.
77 //
78 // It comes from the electorate rather than from the chain so that a realm
79 // whose clock can be fast-forwarded moves the VOTES with it. Reading
80 // runtime.ChainHeight() here instead would leave a test chain able to age
81 // everything except the one thing a dispute waits on, and would let the
82 // governor and the ledger disagree about what epoch it is.
83 Height() int64
84
85 // Now is the block's wall-clock time in unix seconds, from the same source
86 // as Height and for the same reason: a realm that can fast-forward its clock
87 // must move the VOTES with it, and a pure package may not read the chain
88 // directly without leaving a test chain able to age everything except the
89 // one thing a dispute waits on.
90 //
91 // It exists because a voting window is a PROMISE TO A VOTER — "this closes
92 // in four days" — and a height only means a date if the chain's pace never
93 // changes. The governor gates on this and keeps the height beside it.
94 Now() int64
95}
96
97// Token is what a page needs to name the thing being voted: an identity and a
98// live supply. Separate from Electorate because they answer about different
99// times — Electorate about a sealed epoch, Token about now — and a realm may
100// well have one without the other.
101//
102// p/kourt/grc20votes.Ledger satisfies both, which is the ordinary case.
103type Token interface {
104 Name() string
105 Symbol() string
106 Decimals() int
107 TotalSupply() int64
108}
109
110// New creates a governor with no kinds adopted and nothing proposed, and
111// installs the built-in kinds that govern the governor itself.
112//
113// The minter kind is NOT among them: who may create supply is a policy about a
114// caller, and a pure package has no caller. A realm wanting one registers it
115// with InstallBuiltin.
116func New(voters Electorate, token Token) *Governor {
117 if voters == nil || token == nil {
118 panic("governor: a governor needs an electorate and a token")
119 }
120 g := &Governor{
121 voters: voters,
122 token: token,
123 kinds: bptree.NewBPTree32(),
124 proposals: bptree.NewBPTree32(),
125 openIdx: bptree.NewBPTree32(),
126 }
127 g.installBuiltins()
128 return g
129}
130
131// InstallBuiltin adopts a kind under the reserved prefix, which Offer refuses.
132//
133// For the consuming realm only, and it is unexported state that makes that
134// true: reaching this needs the *Governor, which the realm keeps to itself.
135// A realm uses it for a power that is its own rather than the engine's — the
136// mint being the example.
137func (g *Governor) InstallBuiltin(k Kind, r Rules) {
138 name := k.Name()
139 if !isReserved(name) {
140 panic("governor: a built-in has to carry the reserved prefix")
141 }
142 mustBeUsableName(name)
143 g.mustBeSaneRules(r)
144 g.kinds.Set(name, &entry{kind: k, rules: r, live: true})
145}
146