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

valset.gno

5.46 Kb · 145 lines
  1package params
  2
  3import (
  4	"chain"
  5	"errors"
  6	"strconv"
  7	"strings"
  8
  9	prms "sys/params"
 10
 11	"gno.land/p/sys/validators/v0"
 12)
 13
 14// Param keys read by gno.land/pkg/gnoland (EndBlocker).
 15// Keep in sync with gno.land/pkg/gnoland/node_params.go.
 16// nodeModulePrefix is declared in halt.gno (same package).
 17const (
 18	valsetSubmodule = "valset"
 19
 20	// dirty signals the chain that the proposed valset differs from the
 21	// current applied one. Realm sets true; EndBlocker clears.
 22	valsetDirtyKey = "dirty"
 23
 24	// One []string per slot; each entry has the form "<pubkey>:<power>"
 25	// (bech32 pubkey + decimal power). Address is derived from pubkey
 26	// on the chain side and not stored.
 27	//
 28	//   proposed = v0's full target valset
 29	//   current  = chain-managed: the set that becomes ACTIVE AT H+2
 30	//              once the most recent EndBlock's updates apply.
 31	//              NOT necessarily the set actively signing the current
 32	//              block — see ABCI H+2 sequencing.
 33	valsetProposedKey = "proposed"
 34	valsetCurrentKey  = "current"
 35
 36	// pubkey_types: chain-mirrored validator pubkey-type allow-list (read-only here).
 37	valsetPubKeyTypesKey = "pubkey_types"
 38
 39	// Only this realm may write valset:proposed and valset:dirty.
 40	// valset:current is chain-managed (see ctx-sentinel in node_params.go).
 41	valsetAuthorizedRealm = "gno.land/r/sys/validators/v0"
 42)
 43
 44// SetValsetProposal publishes the realm's desired valset. Each entry is
 45// "<bech32-pubkey>:<decimal-power>"; power=0 removes the validator.
 46// The chain reads this on the next EndBlocker, diffs it against
 47// valset:current, and propagates the changes to consensus.
 48func SetValsetProposal(cur realm, entries []string) {
 49	assertValsetCaller(0, cur)
 50	prms.SetSysParamStrings(nodeModulePrefix, valsetSubmodule, valsetProposedKey, entries)
 51	prms.SetSysParamBool(nodeModulePrefix, valsetSubmodule, valsetDirtyKey, true)
 52}
 53
 54// GetValsetEntries returns the chain's authoritative committed
 55// validator set (the contents of valset:current). This is the
 56// V_{H+2} view — the set that will be active at H+2 once the most
 57// recent EndBlock's updates apply, NOT the set signing the current
 58// block. Callers that want "what v0 reports as the current
 59// validator set" — including the in-flight proposed set during
 60// the dirty window — should call GetValsetEffective instead.
 61func GetValsetEntries() []validators.Validator {
 62	return parseValsetSlot(valsetCurrentKey)
 63}
 64
 65// ValsetDirty reports whether valset:proposed is awaiting EndBlocker.
 66// Realm callers MUST treat this as transient: the dirty flag is set
 67// by SetValsetProposal and cleared by the chain's EndBlocker (every
 68// block where dirty=true on entry exits with dirty=false).
 69func ValsetDirty() bool {
 70	d, _ := prms.GetSysParamBool(nodeModulePrefix, valsetSubmodule, valsetDirtyKey)
 71	return d
 72}
 73
 74// GetValsetEffective returns the set that WILL be active at H+2:
 75// valset:proposed if dirty, else valset:current. Used by v0 so that
 76// (a) reads after a same-block proposal callback see that proposal's
 77// effects, and (b) sequential same-block proposals accumulate
 78// correctly on top of each other.
 79//
 80// Misuse warning: this exists for r/sys/validators/v0's internal
 81// reads. Other realms making "is X a validator" decisions should
 82// call v0.IsValidator, not this directly, so future changes to v0's
 83// read semantics propagate uniformly.
 84func GetValsetEffective() []validators.Validator {
 85	key := valsetCurrentKey
 86	if ValsetDirty() {
 87		key = valsetProposedKey
 88	}
 89	return parseValsetSlot(key)
 90}
 91
 92func parseValsetSlot(key string) []validators.Validator {
 93	raw, _ := prms.GetSysParamStrings(nodeModulePrefix, valsetSubmodule, key)
 94	out := make([]validators.Validator, 0, len(raw))
 95	for _, e := range raw {
 96		v, err := parseEntry(e)
 97		if err != nil {
 98			panic("valset:" + key + " corrupted: " + err.Error())
 99		}
100		out = append(out, v)
101	}
102	return out
103}
104
105// parseEntry splits "<bech32-pubkey>:<decimal-power>" and derives the
106// validator address via the chain.PubKeyAddress native helper.
107func parseEntry(entry string) (validators.Validator, error) {
108	pkStr, pStr, ok := strings.Cut(entry, ":")
109	if !ok {
110		return validators.Validator{}, errors.New("missing ':' separator in " + entry)
111	}
112	addr, err := chain.PubKeyAddress(pkStr)
113	if err != nil {
114		return validators.Validator{}, err
115	}
116	power, err := strconv.ParseUint(pStr, 10, 64)
117	if err != nil {
118		return validators.Validator{}, err
119	}
120	return validators.Validator{
121		Address:     addr,
122		PubKey:      pkStr,
123		VotingPower: power,
124	}, nil
125}
126
127// GetValsetPubKeyTypes returns the validator pubkey-type allow-list mirrored from consensus params (empty means accept any).
128func GetValsetPubKeyTypes() []string {
129	types, _ := prms.GetSysParamStrings(nodeModulePrefix, valsetSubmodule, valsetPubKeyTypesKey)
130	return types
131}
132
133// assertValsetCaller authorizes the one realm permitted to write valset params.
134//
135// Delegates to assertDelegate (see delegate.gno) so this realm has exactly one
136// authorized-caller gate rather than two that can drift. The distinction that
137// remains is deliberate and is NOT expressible through a delegation slot: this
138// authorization is a compile-time const, so re-pointing it requires a chain
139// relaunch, and assertNotValsetKey locks GovDAO out of the key family entirely.
140// A vote-settable slot would let one supermajority re-point the valset writer at
141// an arbitrary realm and bypass v0's valoper-existence check, its KeepRunning
142// opt-out, and execution-time pubkey re-resolution.
143func assertValsetCaller(_ int, rlm realm) {
144	assertDelegate(0, rlm, valsetAuthorizedRealm, "valset params")
145}