Search Apps Documentation Source Content File Folder Download Copy Actions Download

valset.gno

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