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

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

introspect.gno

9.09 Kb · 210 lines
  1package governor
  2
  3// What a consumer can ask that the pages already say.
  4//
  5// Render publishes most of this — the slot count, a proposal's epoch, why one
  6// ended — and a caller that has to scrape a markdown page for a number the
  7// engine holds is an engine with an incomplete API. These are the reads that
  8// close that gap.
  9//
 10// None of them hands back a pointer. That is the rule the whole package is
 11// built to (gno-security-guide.md §3(B)): a /p/-declared type can be named by
 12// another /p/ package, so an exported *proposal or a live tree handle would let
 13// a stranger declare a mutator over it and have the storage-realm borrow run it
 14// under the CONSUMING realm's authority. Counts, copies and strings only.
 15
 16// The board's dimensions, which the front page prints and a caller may want to
 17// reason about before proposing.
 18const (
 19	// MaxLive is how many proposals may stand at once.
 20	MaxLive = maxLive
 21
 22	// MaxOpen is how many of those an ordinary kind may fill. The difference
 23	// is kept for the governor's own kinds, so a flood of one thing cannot
 24	// stop the holders changing the rules.
 25	MaxOpen = maxOpen
 26
 27	// GovLanes is that difference.
 28	GovLanes = govLanes
 29
 30	// The bounds on a proposal's three strings, which doc.gno publishes as a
 31	// table because they decide what a proposer can say before finding out by
 32	// transaction.
 33	MaxTitle    = maxTitle
 34	MaxPayload  = maxPayload
 35	MaxKindName = maxKindName
 36
 37	// MaxBatch is how many members one govern:batch may carry.
 38	MaxBatch = maxBatch
 39
 40	// Bps is the basis-point scale every bar here is expressed in.
 41	Bps = bps
 42
 43	// MaxReason is the longest failure message a kind can put on the record.
 44	// A kind returns the string and the governor keeps it for the life of the
 45	// realm, so it is clipped rather than trusted.
 46	MaxReason = maxReason
 47
 48	// Reserved is the prefix the governor's own kinds carry, which Offer
 49	// refuses so that nothing published can render as a built-in.
 50	Reserved = reserved
 51)
 52
 53// OpenSlots is how many proposals are holding a slot, decided-and-unrun
 54// included. The number the front page prints beside MaxLive.
 55func (g *Governor) OpenSlots() int { return g.openIdx.Size() }
 56
 57// Proposals is how many questions have ever been asked here.
 58func (g *Governor) Proposals() int { return g.proposals.Size() }
 59
 60// StoredState is the state that has been WRITTEN DOWN, which is not always the
 61// state a reader is shown.
 62//
 63// State computes the outcome from the rules and the clock, because a proposal
 64// whose deadline passed is decided whether or not anybody has poked it. Only a
 65// transaction records that, so between the deadline and the next Settle the two
 66// differ — and the difference is the whole reason reading is free: a read works
 67// out the answer and stores nothing.
 68//
 69// A caller wanting to know whether the slot has actually been given back, or
 70// whether there is anything left for Settle to do, wants this one.
 71func (g *Governor) StoredState(id int64) string { return stateName(g.mustProposal(id).state) }
 72
 73// Reason is why a proposal ended, or the empty string while it is still open.
 74// The page prints it; this is the same string.
 75func (g *Governor) Reason(id int64) string { return g.mustProposal(id).reason }
 76
 77// EpochOf is the sealed epoch a proposal's weight was read at. Fixed when the
 78// question was asked, and the reason buying in afterwards buys nothing.
 79func (g *Governor) EpochOf(id int64) uint32 { return g.mustProposal(id).epoch }
 80
 81// Snapshot is a proposal's engaged weight and the sealed epoch both it and the
 82// votes were read at — snapshotted at Propose and reachable no other way (EpochOf
 83// gives the epoch alone). Engaged is the turnout DENOMINATOR the pool is shown
 84// against; for a proposal opened with an absolute quorum floor the bar is that
 85// floor, not a fraction of engaged — read it with QuorumFloor. Scalars, so nothing
 86// is handed out to write through.
 87func (g *Governor) Snapshot(id int64) (engaged int64, epoch uint32) {
 88	p := g.mustProposal(id)
 89	return p.engaged, p.epoch
 90}
 91
 92// QuorumFloor is the absolute turnout a proposal needs when it was opened with
 93// ProposeWithQuorum, or 0 when the quorum is the rules' QuorumBps fraction of the
 94// engaged weight. A court renders a floored proposal's bar from this; the engine
 95// already uses it in the tally and on its own page. Snapshotted at Propose, so it
 96// is the bar the voters actually face.
 97func (g *Governor) QuorumFloor(id int64) int64 { return g.mustProposal(id).quorumFloor }
 98
 99// RollSize is how many addresses are on a proposal's voter roll.
100//
101// Zero can mean two things and HasRoll separates them: nobody has voted yet, or
102// the roll was reclaimed after the proposal finished.
103func (g *Governor) RollSize(id int64) int {
104	p := g.mustProposal(id)
105	if p.voted == nil {
106		return 0
107	}
108	// The sentinel Propose plants is not a voter. See rollSentinel.
109	n := p.voted.Size()
110	if p.voted.Has(rollSentinel) {
111		n--
112	}
113	return n
114}
115
116// HasRoll reports whether a proposal is still holding its voter roll, or has
117// given it back through ReleaseRoll.
118func (g *Governor) HasRoll(id int64) bool { return g.mustProposal(id).voted != nil }
119
120// KindStatus is whether a name has been offered here, and whether the holders
121// have adopted it. Both false for a name nobody has published.
122func (g *Governor) KindStatus(name string) (offered, live bool) {
123	e := g.entryOf(name)
124	if e == nil {
125		return false, false
126	}
127	return true, e.live
128}
129
130// KindRules is the terms a kind was adopted on. A copy: Rules is a plain struct
131// of numbers, so handing one back gives away nothing to write through.
132func (g *Governor) KindRules(name string) (Rules, bool) {
133	e := g.entryOf(name)
134	if e == nil {
135		return Rules{}, false
136	}
137	return e.rules, true
138}
139
140// Digest is the key a question occupies in the open index, derived from the
141// kind and payload that define it. Exported because it is a pure function of
142// two public strings, and because what it is FOR is worth knowing: a proposer
143// chooses their payload and so chooses their key, which is why the slot sweep
144// rotates rather than always starting at the low end.
145func (g *Governor) Digest(kind, payload string) string { return g.digest(kind, payload) }
146
147// Sweep reclaims the slots of proposals that have finished but were never
148// written down.
149//
150// Propose already calls it when the board is full, which is the path that
151// matters. This is the same thing on request, for a caller who would rather
152// free a slot than be refused one — permissionless, like Settle, and for the
153// same reason: it decides nothing, it only writes down what the rules already
154// decided.
155func (g *Governor) Sweep() { g.sweep() }
156
157// SetElectorate changes where voting weight comes from, keeping everything
158// else — the adopted kinds, every proposal, the slot cursor.
159//
160// The consuming realm's, like Adopt, and for a narrow purpose: a realm that
161// wraps its ledger to drop idle weight out of the quorum denominator swaps the
162// wrapper in here rather than rebuilding the governor and losing its registry.
163//
164// Only sensible before anything is open. A proposal snapshots the epoch it is
165// weighed at but reads the electorate live, so changing it under an open vote
166// moves the bar the voters were shown — which is the thing per-proposal rules
167// exist to prevent, arrived at from the other side.
168func (g *Governor) SetElectorate(e Electorate) {
169	if e == nil {
170		panic("governor: an electorate is required")
171	}
172	g.voters = e
173}
174
175// Adopt installs a kind as live without a vote.
176//
177// The consuming realm's, and nobody else's — reaching it needs the *Governor,
178// which a realm keeps unexported. That is the same authority the realm already
179// has by holding the engine at all, so this grants nothing new; what it does is
180// make the un-voted path explicit and greppable rather than something a realm
181// improvises by reaching into a tree.
182//
183// For a realm's OWN powers, and for arming a governor in a test. Anything a
184// stranger publishes goes through Offer and an adoption vote, which is the
185// distinction the whole design turns on: publishing code and letting that code
186// hold the governor's authority are different decisions taken by different
187// actors.
188//
189// A realm that exposes this through a crossing function has given governance
190// away. So would exposing Propose without its rules, or Execute without its
191// timelock; the engine cannot stop a consumer publishing the wrong thing, and
192// says so here because this is the one most obviously worth not publishing.
193func (g *Governor) Adopt(k Kind, r Rules) {
194	name := k.Name()
195	mustBeUsableName(name)
196	g.mustBeSaneRules(r)
197	g.kinds.Set(name, &entry{kind: k, rules: r, live: true})
198}
199
200// Render is the front page, or one proposal's page when path names an id.
201//
202// notes are extra lines the consuming realm puts under the token and above the
203// governance — who may mint, most of all, which is the realm's fact rather than
204// the engine's and cannot be inferred from silence.
205//
206// Passed in rather than stored. A stored note is a WRITE, and this is a read: a
207// realm calling SetNotes from its own Render turned reading a page into
208// something that costs storage, which is the defect check-storage exists to
209// catch and did.
210func (g *Governor) Render(path, notes string) string { return g.render(path, notes) }