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

valset.gno

5.39 Kb · 147 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"
 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 = v3'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/v3"
 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 v3 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 v3 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/v3's internal
 81// reads. Other realms making "is X a validator" decisions should
 82// call v3.IsValidator, not this directly, so future changes to v3'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
133func assertValsetCaller(_ int, rlm realm) {
134	// Defense-in-depth IsCurrent gate. Current call sites all pass live
135	// cur (cross(cur) from cache.gno / proposal.gno), so this never
136	// fires today — but the helper trusts its rlm input, and a future
137	// caller that threads a stashed/sibling-frame realm value would
138	// silently bypass the PkgPath check below (Class-2 designation
139	// forgery; see docs/resources/gno-security.md). Gating here makes
140	// the precondition enforceable rather than convention.
141	if !rlm.IsCurrent() {
142		panic("unauthorized: rlm is not the caller's live cur")
143	}
144	if rlm.Previous().PkgPath() != valsetAuthorizedRealm {
145		panic("unauthorized: only " + valsetAuthorizedRealm + " may write valset params")
146	}
147}