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

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)) }