PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidators

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6/governor/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6 / governor
Files
8 (gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6/governor/v0
gno
0.9

Files (8)

  • gnomod.tomltoml
  • batch.gnogno
  • builtin.gnogno
  • electorate.gnogno
  • governor.gnogno
  • introspect.gnogno
  • keys.gnogno
  • rules.gnogno
  • rules.gnogno
    1package governor23import (4	"strconv"5	"strings"67	ufmt "gno.land/p/nt/ufmt/v0"8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    )
    9
    10// rulesKind changes what it takes to pass a kind.
    11//
    12// govern:rules "<kind> quorum=2000 threshold=6600 voting=120960 delay=34560 grace=241920 propose=100"
    13//
    14// All six terms. ProposeBps was once missing, and it is where a bad value is
    15// worst: above a hundred percent nobody can open a question under that kind at
    16// all — adopted, unusable, and unfixable by the one kind that changes terms.
    17//
    18// Fields left out keep their value, so lengthening one delay is a short line.
    19//
    20// Space-separated key=value because a governance payload has to be readable
    21// where it is read — a URL, a wallet's confirmation screen, a forum post. JSON
    22// puts a parser between a voter and their intent.
    23type rulesKind struct{}
    24
    25func (k rulesKind) Name() string { return reserved + "rules" }
    26
    27// Describe renders the requested change and NOTHING about the world.
    28//
    29// Deliberately not "from 20% to 30%": that would read live state, so two
    30// voters at different heights would see different questions at the same URL,
    31// and a proposal that lands in between would silently rewrite what everyone
    32// else is voting on.
    33func (k rulesKind) describe(g *Governor, payload string) string {
    34 name, set, err := parseRules(payload)
    35 if err != nil {
    36 return "malformed rules change: " + err.Error()
    37 }
    38 out := "change the rules for `" + name + "`:"
    39 for _, f := range set {
    40 out += ufmt.Sprintf("\n- %s -> %s", f.key, f.human())
    41 }
    42 return out
    43}
    44
    45func (k rulesKind) check(g *Governor, payload string) error {
    46 name, set, err := parseRules(payload)
    47 if err != nil {
    48 return err
    49 }
    50 e := g.entryOf(name)
    51 if e == nil {
    52 return errNoSuchKind
    53 }
    54 if !e.live {
    55 // Retuning something not yet adopted would be the same swap the
    56 // immutable-name rule closes, moved from the code to the terms: change
    57 // a kind's quorum while its adoption vote is open and holders approve
    58 // one set of terms and get another. What is not adopted is not the
    59 // governor's to tune.
    60 return errNotLive
    61 }
    62 // No check for an empty change set. parseRules refuses a payload with
    63 // fewer than two fields, and every field after the first either parses or
    64 // returns an error, so a nil error here guarantees at least one change. A
    65 // guard would be unreachable, and unreachable defence reads as a case
    66 // somebody thought could happen.
    67 next := e.rules
    68 applyRules(&next, set)
    69 // The same sanity the offering path enforces, and literally the same code:
    70 // one function, two callers. A second copy is how the two paths come to
    71 // disagree about what a proposer must hold.
    72 return saneRules(next)
    73}
    74
    75func (k rulesKind) run(g *Governor, dispatch Dispatch, payload string) error {
    76 name, set, err := parseRules(payload)
    77 if err != nil {
    78 return err
    79 }
    80 e := g.entryOf(name)
    81 if e == nil {
    82 return errNoSuchKind
    83 }
    84 next := e.rules
    85 applyRules(&next, set)
    86 // Only the entry changes. Proposals already open kept a COPY of the rules
    87 // they were opened under, so this cannot move the bar under a vote that is
    88 // already being cast.
    89 e.rules = next
    90 return nil
    91}
    92
    93type field struct {
    94 key string
    95 n int64
    96}
    97
    98func (f field) human() string {
    99 switch f.key {
    100 case "quorum", "threshold", "propose":
    101 // A percentage as well as the raw figure: basis points are exact and
    102 // nobody reads them. Padded by hand because ufmt has no zero-padding
    103 // and its width digits apply to %s alone, so %02d prints 1 rather than
    104 // 01 and one basis point renders as 0.1% — ten times the truth.
    105 return ufmt.Sprintf("%d bps (%s)", f.n, pct(f.n))
    106 default:
    107 // The same rendering the adoption page uses. It printed a bare "%d
    108 // blocks" while adoptKind printed a duration, so the same field read
    109 // two different ways depending on which page you arrived at.
    110 return span(f.n)
    111 }
    112}
    113
    114func parseRules(payload string) (string, []field, error) {
    115 parts := strings.Fields(payload)
    116 if len(parts) < 2 {
    117 return "", nil, govErr("expected a kind name and at least one key=value")
    118 }
    119 name := parts[0]
    120 var set []field
    121 for _, p := range parts[1:] {
    122 eq := strings.Index(p, "=")
    123 if eq <= 0 || eq == len(p)-1 {
    124 return "", nil, govErr("expected key=value, got " + p)
    125 }
    126 key, raw := p[:eq], p[eq+1:]
    127 switch key {
    128 case "quorum", "threshold", "voting", "delay", "grace", "propose":
    129 default:
    130 return "", nil, govErr("unknown field " + key)
    131 }
    132 n, err := strconv.ParseInt(raw, 10, 64)
    133 if err != nil || n < 0 {
    134 return "", nil, govErr("expected a non-negative number for " + key)
    135 }
    136 set = append(set, field{key: key, n: n})
    137 }
    138 return name, set, nil
    139}
    140
    141// applyRules applies an already-validated change set to r. It cannot fail:
    142// parseRules has validated every key and value, so a default/error path here would
    143// be unreachable (unreachable defence reads as a case somebody thought could happen).
    144func applyRules(r *Rules, set []field) {
    145 for _, f := range set {
    146 switch f.key {
    147 case "quorum":
    148 r.QuorumBps = f.n
    149 case "threshold":
    150 r.ThresholdBps = f.n
    151 case "voting":
    152 r.VotingBlocks = f.n
    153 case "delay":
    154 r.DelayBlocks = f.n
    155 case "grace":
    156 r.GraceBlocks = f.n
    157 case "propose":
    158 r.ProposeBps = f.n
    159 }
    160 }
    161}
    162
    163// pct renders basis points as a percentage.
    164//
    165// Padded by hand because ufmt has no zero-padding and its width digits apply
    166// to %s alone, so %02d prints 1 rather than 01 and one basis point reads as
    167// 0.1% — ten times the truth, on the line a voter actually reads.
    168func pct(n int64) string {
    169 // n/(bps/100) is the percentage; bps/100 is 100, so the split is n/100 and
    170 // n%100. Derived from the constant rather than written as 100, so a change
    171 // to the scale cannot leave this rendering the old one.
    172 per := bps / 100
    173 frac := strconv.FormatInt(n%per, 10)
    174 for int64(len(frac)) < 2 {
    175 frac = "0" + frac
    176 }
    177 return ufmt.Sprintf("%d.%s%%", n/per, frac)
    178}
    179