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

valopers state

Back to all declarations

Auth

func() string

Auth returns a read-only description of the realm's current governance authority (for rendering / inspection). It renders as contract_authority[contract=gno.land/r/gov/dao,proposer=contract-identity] i.e. "GovDAO governs this realm, and only GovDAO may drive it" — a statement an on-chain reader can act on. The proposer half is load-bearing: without it the string is byte-identical whether the authority is gated or wide open (see ContractAuthority.String). It deliberately does NOT return the live \*authz.Authorizer: an exported handle to the authority would let any realm reach its mutators (Transfer, DoByPrevious, ...) directly. Returning a description instead of the handle keeps that surface unreachable from here in the first place. Valoper.AuthOwner follows the same rule for per-operator auth lists.

Open
OID
023699…434b:4
Auth details

Inspect func

updateInstructions

func(int, rlm .uverse.realm, newInstructions string) .uverse.error

updateInstructions is the realm's only privileged write. It authorizes as the realm that CROSSED INTO valopers, not as valopers itself. Why DoByPrevious and not DoByCurrent: own-path + DoByCurrent is a tautology (see NewContractAuthority's godoc for the mechanism, once). \`rlm.Previous()\` is the caller — on the legitimate path r/gov/dao's executor frame, which is what init.gno's authority is pointed at. What it buys: it is the code-level guard against this realm's most likely regression, a maintainer later adding an exported crossing entrypoint that reaches here. Under DoByCurrent such an entrypoint authorizes for any caller; under DoByPrevious the caller's own address is the principal and the gate rejects it. Pinned by filetests/z\_govdao\_only\_principal\_filetest.gno. Two things it does NOT cover on its own, both closed elsewhere: re-exporting the privileged closure (guarded by the seal in NewInstructionsProposalRequest), and exporting a function whose signature is assignable to dao's \`func(realm) error\` callback type, which the proxy would invoke on an attacker's behalf. The second was a live bypass of this gate until SimpleExecutor.Execute began rejecting invocation from outside r/gov/dao (r/gov/dao/types.gno); keep BOTH conditions in mind before adding an exported symbol here. Returns the authorization error rather than panicking on it. That matters on the governance path: impl.ExecuteProposal turns an executor ERROR into status Denied plus a DeniedReason, but it cannot see a panic — a panic aborts the whole ExecuteProposal transaction, burning its gas and leaving the proposal stuck Accepted, retryable forever and never deniable. The refusal is currently unreachable on the approved route (Previous() there is always r/gov/dao), but init.gno's handler is explicitly offered as the seam for an intent check (timelock, quorum, audit trail), and the first such check to return an error would hit exactly that.

Open
OID
023699…434b:6
updateInstructions details

Inspect func

NewInstructionsProposalRequest

func(newInstructions string) dao.ProposalRequest

NewInstructionsProposalRequest builds the GovDAO proposal that rewrites this realm's instructions. It replaces the previously exported NewInstructionsProposalCallback. SECURITY: the privileged closure must never leave this package. The VM mints a crossing frame's \`cur\` from the CALLEE's declaring package, so a closure declared here always runs with valopers' identity no matter who invokes it — and whoever holds the closure chooses its Previous() by wrapping it in an executor of their own. Handing such a closure to a caller therefore hands out the ability to satisfy this realm's gate whichever principal it is pointed at: the handler in init.gno runs the action and \`instructions\` is rewritten with no proposal and no vote. That is what the exported callback did. Returning a dao.ProposalRequest instead seals the capability: the executor is an unexported field with no accessor (dao.ProposalRequest exposes only Title/Description/Filter), so the only way to reach the closure is for GovDAO to execute the proposal that contains it. Note this is deliberately stronger than returning a dao.Executor — even a dao.NewSafeExecutor-wrapped one. Executor.Execute is an exported method, so a returned Executor stays directly invocable by its holder, and SafeExecutor's only gate (InAllowedDAOs) FAILS OPEN while the allowedDAOs list is empty, which is the documented bootstrap state (see r/gov/dao/loader/v0). A sealed request has no such conditional. The rule for this realm: no exported function may return a crossing closure, a dao.Executor, or anything else that carries valopers' frame identity — and no exported function may itself BE such a thing, i.e. have a signature assignable to dao's \`func(realm) error\` callback type, since the proxy would then invoke it on an attacker's behalf. filetests/z\_foreign\_realm\_capability\_filetest.gno pins the first; filetests/z\_govdao\_only\_principal\_filetest.gno pins the second.

Open
OID
023699…434b:7
NewInstructionsProposalRequest details

Inspect func

instructionsProposalTitle

untyped string

instructionsProposalTitle is the on-chain title of the instructions proposal, named as a constant so a test can pin it: it is rendered to GovDAO voters and is the string off-chain tooling matches on. It was "/p/gnops/valopers: ..." before this realm's proposal construction moved here; the "/p/" was simply wrong (valopers is an /r/ realm).

Value

"/r/gnops/valopers: Update instructions"

rotateAuthority

func(int, rlm .uverse.realm, newContractPath string) .uverse.error

rotateAuthority replaces this realm's governance authority. Unexported, and reachable only through the sealed request built by NewAuthorityRotationProposalRequest — same discipline as updateInstructions, for the same reason. authz.Authorizer.Transfer derives its principal as rlm.Previous().Address(), so inside a GovDAO-executed callback declared here Previous() is r/gov/dao — exactly the principal init.gno's authority asserts. No same-package cross() trick is needed.

Open
OID
023699…434b:8
rotateAuthority details

Inspect func

NewAuthorityRotationProposalRequest

func(newContractPath string) dao.ProposalRequest

NewAuthorityRotationProposalRequest builds the GovDAO proposal that re-points this realm's governance authority at \`newContractPath\`. WHY THIS EXISTS. Without it the authority installed by init.gno is permanently frozen: nothing else calls auth.Transfer, Auth() returns a description rather than the live Authorizer, and Transfer routes through the authority's own gate — so if the asserted principal ever stops being presentable, \`instructions\` becomes unwritable with no on-chain recovery. That is not hypothetical: the r/gov/dao proxy path is a governance artifact that can be superseded, and a well-formed but wrong path (say the impl path instead of the proxy) is accepted at construction and dead forever. Redeploying instead is expensive — r/sys/validators/v0/cache.gno hardcodes \`const valopersRealmPath\`, so a new pkgpath means editing a second genesis realm and re-registering every valoper. It must exist before deploy, because code cannot be added to a deployed realm. WHY A PATH AND NOT AN authz.Authority. Taking an Authority would make this an open-interface input (Class-3 impl-substitution): a proposal could install an always-approve or always-deny authority, and readers of Auth() could not tell from the rendered description alone. Taking a path keeps the installed authority canonical by construction — always a ContractAuthority with the pass-through handler and the contract-identity gate — so the only thing governance can change is WHICH principal is asserted. The path is validated by authz.NewContractAuthority, which panics on a malformed one, aborting the proposal execution rather than bricking the realm. This remains a privileged surface: a majority that can pass this proposal can point the authority at a principal it controls. That is the same power a majority already has over \`instructions\`, and it is the price of being recoverable at all.

Open
OID
023699…434b:9
NewAuthorityRotationProposalRequest details

Inspect func

MonikerMaxLength

untyped bigint

Value

(32 <untyped> bigint)

DescriptionMaxLength

untyped bigint

Value

(2048 <untyped> bigint)

ServerTypeCloud

untyped string

Value

"cloud"

ServerTypeOnPrem

untyped string

Value

"on-prem"

ServerTypeDataCenter

untyped string

Value

"data-center"

ErrValoperExists

*errors.errorString
Open
OID
023699…434b:13
ErrValoperExists details

Inspect pointer

ErrValoperMissing

*errors.errorString
Open
OID
023699…434b:16
ErrValoperMissing details

Inspect pointer

ErrInvalidAddress

*errors.errorString
Open
OID
023699…434b:19
ErrInvalidAddress details

Inspect pointer

ErrInvalidMoniker

*errors.errorString
Open
OID
023699…434b:22
ErrInvalidMoniker details

Inspect pointer

ErrInvalidDescription

*errors.errorString
Open
OID
023699…434b:25
ErrInvalidDescription details

Inspect pointer

ErrInvalidServerType

*errors.errorString
Open
OID
023699…434b:28
ErrInvalidServerType details

Inspect pointer

ErrOperatorSquatGuard

*errors.errorString
Open
OID
023699…434b:31
ErrOperatorSquatGuard details

Inspect pointer

ErrSigningKeyTaken

*errors.errorString
Open
OID
023699…434b:34
ErrSigningKeyTaken details

Inspect pointer

ErrFrontrunValidator

*errors.errorString
Open
OID
023699…434b:37
ErrFrontrunValidator details

Inspect pointer

ErrRotationThrottled

*errors.errorString
Open
OID
023699…434b:40
ErrRotationThrottled details

Inspect pointer

ErrRegistryEntryMissing

*errors.errorString
Open
OID
023699…434b:43
ErrRegistryEntryMissing details

Inspect pointer

ErrPaidCallNotDirect

*errors.errorString
Open
OID
023699…434b:46
ErrPaidCallNotDirect details

Inspect pointer

ErrFeeParamOutOfRange

*errors.errorString
Open
OID
023699…434b:49
ErrFeeParamOutOfRange details

Inspect pointer

ErrDisallowedPubKeyType

*errors.errorString
Open
OID
023699…434b:52
ErrDisallowedPubKeyType details

Inspect pointer

valopers

*v0.Tree

// operator-address -> Valoper

Open
OID
023699…434b:158
valopers details

Inspect pointer

instructions

string

// markdown instructions for valoper's registration

Value

"\n# Welcome to the **Valopers** realm\n\n## 📌 Purpose of this Contract\n\nThe **Valopers** contract is designed to maintain a registry of **validator profiles**. This registry provides essential information to **GovDAO members**, enabling them to make inform"...

monikerMaxLengthMiddle

string

Value

"30"

validateMonikerRe

*regexp.Regexp

// 32 characters, including spaces, hyphens or underscores in the middle

Open
OID
023699…434b:61
validateMonikerRe details

Inspect pointer

regEntry

type

regEntry tracks signing-address -> operator with retirement metadata. retiredAtHeight == 0 means the entry is currently active for the operator.

Value

valopers.regEntry

Valoper

type

Valoper represents a validator operator profile.

Value

valopers.Valoper

AddToAuthList

func(addr .uverse.address, member .uverse.address)
Open
OID
023699…434b:136
AddToAuthList details

Inspect func

DeleteFromAuthList

func(addr .uverse.address, member .uverse.address)
Open
OID
023699…434b:138
DeleteFromAuthList details

Inspect func

Register

func(moniker string, description string, serverType string, addr .uverse.address, pubKey string)

Register registers a new valoper. The \`addr\` parameter is the operator address (stable identity, profile key); \`pubKey\` is the consensus signing pubkey, from which the signing address is derived. Auth shape: - Post-genesis: OriginCaller must equal addr (operator-slot squat guard). Genesis-mode replay (ChainHeight()==0) bypasses, so migration .jsonl txs and historical Register replays succeed. - Signing-address uniqueness: derived(pubKey) must not already be in signingRegistry, active or retired. - Front-running guard: post-genesis, derived(pubKey) must not already be an active validator (a fresh registration cannot squat on the consensus address of an existing validator). Why OriginCaller==addr is sufficient for the SQUAT guard (no IsUserCall): squatting requires the attacker to satisfy OriginCaller==victim, which requires the victim's signing key. The PAYMENT check is a different matter and does need IsUserCall — see assertPaidCallIsDirect. An earlier version of this comment claimed the fee was "validated against banker.OriginSend in a way that's symmetric to IsUserCall via direct comparison"; there was no such comparison, and the fee was bypassable (reported by @D4ryl00). Auth-list seeding: the profile's Authorizable owner is set to addr (NOT OriginCaller). At H>0 the squat guard makes them equal anyway; at H==0 the deployer pattern (one signer registers many operators) requires owner == addr so each operator can manage their own profile post-genesis without needing the deployer's auth.

Open
OID
023699…434b:139
Register details

Inspect func

UpdateMoniker

func(addr .uverse.address, moniker string)

UpdateMoniker updates an existing valoper's moniker.

Open
OID
023699…434b:140
UpdateMoniker details

Inspect func

UpdateDescription

func(addr .uverse.address, description string)

UpdateDescription updates an existing valoper's description.

Open
OID
023699…434b:141
UpdateDescription details

Inspect func

UpdateKeepRunning

func(addr .uverse.address, keepRunning bool)

UpdateKeepRunning updates an existing valoper's active status. Calls v0.NotifyValoperChanged because the cache stores KeepRunning.

Open
OID
023699…434b:142
UpdateKeepRunning details

Inspect func

UpdateServerType

func(addr .uverse.address, serverType string)

UpdateServerType updates an existing valoper's server type.

Open
OID
023699…434b:143
UpdateServerType details

Inspect func

UpdateSigningKey

func(addr .uverse.address, newPubKey string)

UpdateSigningKey rotates an operator's consensus signing key. Auth: caller must be on the operator's auth list (defaults to operator at Register time; extendable via AddToAuthList). Invariants checked at entry: - throttle: ChainHeight() - v.LastRotationHeight >= rotationPeriodBlocks - signingRegistry uniqueness: derived(newPubKey) not in registry (active OR retired); permanently blocks key reuse - fee: unsafe.OriginSend() >= rotationFee (mirrors Register's fee-check pattern) Effect: profile's SigningPubKey/SigningAddress/LastRotationHeight updated; old registry entry marked retired (retiredAtHeight = ChainHeight()); new entry inserted into signingRegistry; v0 emits remove+add to sysparams via RotateValoperSigningKey; v0 cache refreshed via NotifyValoperChanged. Rotation lands in consensus at H+2. Atomicity: Gno tx atomicity rolls back all state if any step panics. If v0.RotateValoperSigningKey panics, the registry insert and profile mutation revert with it.

Open
OID
023699…434b:144
UpdateSigningKey details

Inspect func

GetByAddr

func(addr .uverse.address) valopers.Valoper

GetByAddr fetches the valoper using the operator address, if present.

Open
OID
023699…434b:145
GetByAddr details

Inspect func

Render

func(fullPath string) string

Render renders a single valoper with their information.

Open
OID
023699…434b:146
Render details

Inspect func

renderHome

func(path string) string
Open
OID
023699…434b:147
renderHome details

Inspect func

isValoper

func(addr .uverse.address) bool

isValoper checks if the valoper exists.

Open
OID
023699…434b:148
isValoper details

Inspect func

validateMoniker

func(moniker string) .uverse.error

validateMoniker checks if the moniker is valid.

Open
OID
023699…434b:149
validateMoniker details

Inspect func

validateDescription

func(description string) .uverse.error

validateDescription checks if the description is valid.

Open
OID
023699…434b:150
validateDescription details

Inspect func

validateBech32

func(addr .uverse.address) .uverse.error

validateBech32 checks if the value is a valid bech32 address.

Open
OID
023699…434b:151
validateBech32 details

Inspect func

validatePubKey

func(pubKey string) .uverse.error

validatePubKey checks if the public key is valid.

Open
OID
023699…434b:152
validatePubKey details

Inspect func

assertPaidCallIsDirect

func(int, rlm .uverse.realm)

assertPaidCallIsDirect requires the immediate caller to be a plain EOA, and must be paired with every unsafe.OriginSend() amount check in this realm. Called only when a fee is actually configured. unsafe.OriginSend() reports the transaction's declared send ENVELOPE, not what this realm received. Only a direct \`maketx call\` on valopers credits the envelope to this realm's address. For a \`maketx run\` the keeper sets pkgAddr := caller (gno.land/pkg/sdk/vm/keeper.go), so the coins move from the caller to the caller and never land anywhere at all, while OriginSend() still reports the full amount to us. See gno.land/adr/pr6062\_payable\_send\_check.md: "MsgRun is exempt: the coins are moved from the caller to the caller, so nothing actually moves." So the amount check alone verifies intent, never receipt. Reported by @D4ryl00, with a local-chain reproduction: governance sets register\_fee to 1000ugnot, an operator runs a script calling Register with \`-send 1000ugnot\`, registration succeeds, valopers receives zero, and the operator still holds the coins. No hostile script is needed — the envelope is free by construction. Both fees default to 0, so nothing was live. Why IsUserCall and not an address comparison: a run script's realm is address-INDISTINGUISHABLE from its user. Inside \`main(cur realm)\` the ephemeral realm's own address IS the caller's EOA address, so every address-based guard in this realm accepts a \`maketx run\` — Register's squat guard (OriginCaller == addr) and UpdateSigningKey's auth-list check (Previous().Address() on the list) both pass. Only the pkgPath differs, and IsUserCall() is the check that reads it. It is therefore the only PreviousRealm shape where the envelope is guaranteed to have landed here: it excludes intermediate code realms and user-run ephemeral realms alike. Same reasoning and same pairing as r/sys/namereg/v0.Register, which reads OriginSend for its anti-squatting payment; see the two-guard comment there. Deliberately inside the \`fee > 0\` branch rather than at the top of the function: while a fee is unset there is no payment to establish receipt of, and gating unconditionally would break the operator-authored \`maketx run\` flows that the squat guard is happy to accept. The restriction appears exactly when, and only when, money is involved. KNOWN LIMIT. AddToAuthList takes a plain \`member address\`, so a REALM can sit on an operator's auth list, and once rotation\_fee is nonzero such a member can no longer drive UpdateSigningKey — it is not a user call. An EOA rotation bot is unaffected, which is the shape the auth-list docs describe ("HSM-bound or rotation-bot"), so nothing documented breaks; a realm-mediated rotation would. The alternative that would keep realms working is a true receipt check: track cumulative fees in realm state and require the realm's own balance to have risen by \`fee\` since the last one. That works for any caller shape, but it puts new persisted state and a banker into a genesis realm, and lets anyone pre-pay another operator's fee by donating to the realm address. Not worth it while both fees are 0 and collected fees are unwithdrawable anyway (this realm has no banker, so they strand at its address). Revisit if a realm-mediated paid rotation is ever actually wanted.

Open
OID
023699…434b:153
assertPaidCallIsDirect details

Inspect func

minFeeCoin

func(fee uint64) chain.Coin

minFeeCoin converts a configured fee (uint64, from sysparams) into the ugnot Coin the amount checks compare against. Coin.Amount is an int64. A fee above MaxInt64 wraps negative, and \`sentCoins\[0].IsLT(minFee)\` is then false for every payment — the realm reads the fee as configured, runs assertPaidCallIsDirect, and collects 1ugnot while reporting "payment must not be less than -1ugnot". Failing closed here keeps an unrepresentable fee from opening the paid path instead of closing it. Reachable two ways today, neither requiring malice: governance can set any uint64 through the generic r/sys/params factories, and valopers/proposal.ProposeNewMinFeeProposalRequest takes an int64 and stores uint64(newMinFee), so a negative fee — the obvious way to write "disable the fee" — round-trips to 2^64-1. That signature is preserved for historical-replay compatibility and is deliberately left alone; the bound belongs at the consumption site, which covers both paths.

Open
OID
023699…434b:154
minFeeCoin details

Inspect func

assertPubKeyTypeAllowed

func(pubKey string)

assertPubKeyTypeAllowed panics if pubKey's type is not in the chain's validator allow-list (empty list accepts any).

Open
OID
023699…434b:155
assertPubKeyTypeAllowed details

Inspect func

pubKeyTypeURL

func(pubKey string) (string, .uverse.error)

pubKeyTypeURL returns the amino type URL (e.g. "/tm.PubKeyEd25519") of a bech32 consensus pubkey.

Open
OID
023699…434b:156
pubKeyTypeURL details

Inspect func

validateServerType

func(serverType string) .uverse.error

validateServerType checks if the server type is valid.

Open
OID
023699…434b:157
validateServerType details

Inspect func
auth : *v0.Authorizer Inspect
Auth : func() string Inspect
updateInstructions : func(int, rlm .uverse.realm, newInstructions string) .uverse.error Inspect
NewInstructionsProposalRequest : func(newInstructions string) dao.ProposalRequest Inspect
instructionsProposalTitle : untyped string ="/r/gnops/valopers: Update instructions"
rotateAuthority : func(int, rlm .uverse.realm, newContractPath string) .uverse.error Inspect
NewAuthorityRotationProposalRequest : func(newContractPath string) dao.ProposalRequest Inspect
init.7 : func() Inspect
MonikerMaxLength : untyped bigint =(32 <untyped> bigint)
DescriptionMaxLength : untyped bigint =(2048 <untyped> bigint)
ServerTypeCloud : untyped string ="cloud"
ServerTypeOnPrem : untyped string ="on-prem"
ServerTypeDataCenter : untyped string ="data-center"
ErrValoperExists : *errors.errorString Inspect
ErrValoperMissing : *errors.errorString Inspect
ErrInvalidAddress : *errors.errorString Inspect
ErrInvalidMoniker : *errors.errorString Inspect
ErrInvalidDescription : *errors.errorString Inspect
ErrInvalidServerType : *errors.errorString Inspect
ErrOperatorSquatGuard : *errors.errorString Inspect
ErrSigningKeyTaken : *errors.errorString Inspect
ErrFrontrunValidator : *errors.errorString Inspect
ErrRotationThrottled : *errors.errorString Inspect
ErrRegistryEntryMissing : *errors.errorString Inspect
ErrPaidCallNotDirect : *errors.errorString Inspect
ErrFeeParamOutOfRange : *errors.errorString Inspect
ErrDisallowedPubKeyType : *errors.errorString Inspect
valopers : *v0.Tree Inspect
instructions : string ="\n# Welcome to the **Valopers** realm\n\n## 📌 Purpose of this Contract\n\nThe **Valopers** contract is designed to maintain a registry of **validator profiles**. This registry provides essential information to **GovDAO members**, enabling them to make inform"...
signingRegistry : *v0.BPTree Inspect
monikerMaxLengthMiddle : string ="30"
validateMonikerRe : *regexp.Regexp Inspect
regEntry : type =valopers.regEntry
Valoper : type =valopers.Valoper
AddToAuthList : func(addr .uverse.address, member .uverse.address) Inspect
DeleteFromAuthList : func(addr .uverse.address, member .uverse.address) Inspect
Register : func(moniker string, description string, serverType string, addr .uverse.address, pubKey string) Inspect
UpdateMoniker : func(addr .uverse.address, moniker string) Inspect
UpdateDescription : func(addr .uverse.address, description string) Inspect
UpdateKeepRunning : func(addr .uverse.address, keepRunning bool) Inspect
UpdateServerType : func(addr .uverse.address, serverType string) Inspect
UpdateSigningKey : func(addr .uverse.address, newPubKey string) Inspect
GetByAddr : func(addr .uverse.address) valopers.Valoper Inspect
Render : func(fullPath string) string Inspect
renderHome : func(path string) string Inspect
isValoper : func(addr .uverse.address) bool Inspect
validateMoniker : func(moniker string) .uverse.error Inspect
validateDescription : func(description string) .uverse.error Inspect
validateBech32 : func(addr .uverse.address) .uverse.error Inspect
validatePubKey : func(pubKey string) .uverse.error Inspect
assertPaidCallIsDirect : func(int, rlm .uverse.realm) Inspect
minFeeCoin : func(fee uint64) chain.Coin Inspect
assertPubKeyTypeAllowed : func(pubKey string) Inspect
pubKeyTypeURL : func(pubKey string) (string, .uverse.error) Inspect
validateServerType : func(serverType string) .uverse.error Inspect