Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

electorate.gno

6.51 Kb · 145 lines
  1package governor
  2
  3import (
  4	bptree "gno.land/p/nt/bptree/v0"
  5)
  6
  7// Electorate is where voting weight comes from, and the only thing tying this
  8// 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}