Kind Realm (renderable)
Name valopers
Namespace gnops
Exported functions 13
Module gno.land/r/gnops/valopers
gno 0.9 admin.gno gno
⧉
1 package valopers 2 3 import ( 4 "gno.land/p/moul/authz/v0" 5 "gno.land/p/nt/ufmt/v0" 6 "gno.land/r/gov/dao" 7 ) 8 AddToAuthList (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, member string)
Auth () string
DeleteFromAuthList (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, member string)
GetByAddr (addr string) struct{Moniker string; Description string; ServerType string; OperatorAddress .uverse.address; SigningPubKey string; SigningAddress .uverse.address; LastRotationHeight int64; KeepRunning bool; auth *gno.land/p/nt/ownable/exts/authorizable/v0.Authorizable}
NewAuthorityRotationProposalRequest (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newContractPath string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
NewInstructionsProposalRequest (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newInstructions string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
Register (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, moniker string, description string, serverType string, addr string, pubKey string)
Render (fullPath string) string
UpdateDescription (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, description string)
UpdateKeepRunning (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, keepRunning bool)
UpdateMoniker (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, moniker string)
UpdateServerType (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, serverType string)
UpdateSigningKey (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, newPubKey string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
9 var auth *authz.Authorizer
10
11 // Auth returns a read-only description of the realm's current governance
12 // authority (for rendering / inspection). It renders as
13 //
14 // contract_authority[contract=gno.land/r/gov/dao,proposer=contract-identity]
15 //
16 // i.e. "GovDAO governs this realm, and only GovDAO may drive it" — a
17 // statement an on-chain reader can act on. The proposer half is
18 // load-bearing: without it the string is byte-identical whether the
19 // authority is gated or wide open (see ContractAuthority.String).
20 //
21 // It deliberately does NOT return the live *authz.Authorizer: an
22 // exported handle to the authority would let any realm reach its
23 // mutators (Transfer, DoByPrevious, ...) directly. Returning a
24 // description instead of the handle keeps that surface unreachable from
25 // here in the first place. Valoper.AuthOwner follows the same rule for
26 // per-operator auth lists.
27 func Auth() string {
28 return auth.String()
29 }
30
31 // updateInstructions is the realm's only privileged write. It authorizes
32 // as the realm that CROSSED INTO valopers, not as valopers itself.
33 //
34 // Why DoByPrevious and not DoByCurrent: own-path + DoByCurrent is a
35 // tautology (see NewContractAuthority's godoc for the mechanism, once).
36 // `rlm.Previous()` is the caller — on the legitimate path r/gov/dao's
37 // executor frame, which is what init.gno's authority is pointed at.
38 //
39 // What it buys: it is the code-level guard against this realm's most
40 // likely regression, a maintainer later adding an exported crossing
41 // entrypoint that reaches here. Under DoByCurrent such an entrypoint
42 // authorizes for any caller; under DoByPrevious the caller's own address
43 // is the principal and the gate rejects it. Pinned by
44 // filetests/z_govdao_only_principal_filetest.gno.
45 //
46 // Two things it does NOT cover on its own, both closed elsewhere:
47 // re-exporting the privileged closure (guarded by the seal in
48 // NewInstructionsProposalRequest), and exporting a function whose
49 // signature is assignable to dao's `func(realm) error` callback type,
50 // which the proxy would invoke on an attacker's behalf. The second was a
51 // live bypass of this gate until SimpleExecutor.Execute began rejecting
52 // invocation from outside r/gov/dao (r/gov/dao/types.gno); keep BOTH
53 // conditions in mind before adding an exported symbol here.
54 //
55 // Returns the authorization error rather than panicking on it. That matters
56 // on the governance path: impl.ExecuteProposal turns an executor ERROR into
57 // status Denied plus a DeniedReason, but it cannot see a panic — a panic
58 // aborts the whole ExecuteProposal transaction, burning its gas and leaving
59 // the proposal stuck Accepted, retryable forever and never deniable. The
60 // refusal is currently unreachable on the approved route (Previous() there
61 // is always r/gov/dao), but init.gno's handler is explicitly offered as the
62 // seam for an intent check (timelock, quorum, audit trail), and the first
63 // such check to return an error would hit exactly that.
64 func updateInstructions(_ int , rlm realm, newInstructions string ) error {
65 return auth.DoByPrevious( 0 , rlm, "update-instructions" , func () error {
66 instructions = newInstructions
67 return nil
68 })
69 }
70
71 // NewInstructionsProposalRequest builds the GovDAO proposal that rewrites
72 // this realm's instructions. It replaces the previously exported
73 // NewInstructionsProposalCallback.
74 //
75 // SECURITY: the privileged closure must never leave this package.
76 //
77 // The VM mints a crossing frame's `cur` from the CALLEE's declaring
78 // package, so a closure declared here always runs with valopers'
79 // identity no matter who invokes it — and whoever holds the closure
80 // chooses its Previous() by wrapping it in an executor of their own.
81 // Handing such a closure to a caller therefore hands out the ability to
82 // satisfy this realm's gate whichever principal it is pointed at: the
83 // handler in init.gno runs the action and `instructions` is rewritten
84 // with no proposal and no vote. That is what the exported callback did.
85 //
86 // Returning a dao.ProposalRequest instead seals the capability: the
87 // executor is an unexported field with no accessor (dao.ProposalRequest
88 // exposes only Title/Description/Filter), so the only way to reach the
89 // closure is for GovDAO to execute the proposal that contains it.
90 //
91 // Note this is deliberately stronger than returning a dao.Executor —
92 // even a dao.NewSafeExecutor-wrapped one. Executor.Execute is an
93 // exported method, so a returned Executor stays directly invocable by
94 // its holder, and SafeExecutor's only gate (InAllowedDAOs) FAILS OPEN
95 // while the allowedDAOs list is empty, which is the documented
96 // bootstrap state (see r/gov/dao/loader/v0). A sealed request has no
97 // such conditional.
98 //
99 // The rule for this realm: no exported function may return a crossing
100 // closure, a dao.Executor, or anything else that carries valopers'
101 // frame identity — and no exported function may itself BE such a thing,
102 // i.e. have a signature assignable to dao's `func(realm) error` callback
103 // type, since the proxy would then invoke it on an attacker's behalf.
104 // filetests/z_foreign_realm_capability_filetest.gno pins the first;
105 // filetests/z_govdao_only_principal_filetest.gno pins the second.
106 func NewInstructionsProposalRequest(cur realm, newInstructions string ) dao.ProposalRequest {
107 cb := func (cur realm) error {
108 return updateInstructions( 0 , cur, newInstructions)
109 }
110
111 title := instructionsProposalTitle
112 description := ufmt.Sprintf( "Update the instructions to: \n\n%s" , newInstructions)
113
114 return dao.NewProposalRequest(title, description, dao.NewSimpleExecutor( 0 , cur, cb, "" ))
115 }
116
117 // instructionsProposalTitle is the on-chain title of the instructions
118 // proposal, named as a constant so a test can pin it: it is rendered to
119 // GovDAO voters and is the string off-chain tooling matches on. It was
120 // "/p/gnops/valopers: ..." before this realm's proposal construction moved
121 // here; the "/p/" was simply wrong (valopers is an /r/ realm).
122 const instructionsProposalTitle = "/r/gnops/valopers: Update instructions"
123
124 // rotateAuthority replaces this realm's governance authority.
125 //
126 // Unexported, and reachable only through the sealed request built by
127 // NewAuthorityRotationProposalRequest — same discipline as
128 // updateInstructions, for the same reason.
129 //
130 // authz.Authorizer.Transfer derives its principal as
131 // rlm.Previous().Address(), so inside a GovDAO-executed callback declared
132 // here Previous() is r/gov/dao — exactly the principal init.gno's authority
133 // asserts. No same-package cross() trick is needed.
134 func rotateAuthority(_ int , rlm realm, newContractPath string ) error {
135 return auth.Transfer( 0 , rlm, authz.NewContractAuthority(
136 newContractPath,
137 func (_ string , action authz.PrivilegedAction) error {
138 return action()
139 },
140 ))
141 }
142
143 // NewAuthorityRotationProposalRequest builds the GovDAO proposal that
144 // re-points this realm's governance authority at `newContractPath`.
145 //
146 // WHY THIS EXISTS. Without it the authority installed by init.gno is
147 // permanently frozen: nothing else calls auth.Transfer, Auth() returns a
148 // description rather than the live Authorizer, and Transfer routes through
149 // the authority's own gate — so if the asserted principal ever stops being
150 // presentable, `instructions` becomes unwritable with no on-chain recovery.
151 // That is not hypothetical: the r/gov/dao proxy path is a governance
152 // artifact that can be superseded, and a well-formed but wrong path (say
153 // the impl path instead of the proxy) is accepted at construction and dead
154 // forever. Redeploying instead is expensive — r/sys/validators/v0/cache.gno
155 // hardcodes `const valopersRealmPath`, so a new pkgpath means editing a
156 // second genesis realm and re-registering every valoper. It must exist
157 // before deploy, because code cannot be added to a deployed realm.
158 //
159 // WHY A PATH AND NOT AN authz.Authority. Taking an Authority would make
160 // this an open-interface input (Class-3 impl-substitution): a proposal
161 // could install an always-approve or always-deny authority, and readers of
162 // Auth() could not tell from the rendered description alone. Taking a path
163 // keeps the installed authority canonical by construction — always a
164 // ContractAuthority with the pass-through handler and the contract-identity
165 // gate — so the only thing governance can change is WHICH principal is
166 // asserted. The path is validated by authz.NewContractAuthority, which
167 // panics on a malformed one, aborting the proposal execution rather than
168 // bricking the realm.
169 //
170 // This remains a privileged surface: a majority that can pass this proposal
171 // can point the authority at a principal it controls. That is the same
172 // power a majority already has over `instructions`, and it is the price of
173 // being recoverable at all.
174 func NewAuthorityRotationProposalRequest(cur realm, newContractPath string ) dao.ProposalRequest {
175 cb := func (cur realm) error {
176 return rotateAuthority( 0 , cur, newContractPath)
177 }
178
179 title := "/r/gnops/valopers: Rotate governance authority"
180 description := ufmt.Sprintf(
181 "Re-point the valopers governance authority at: \n\n%s\n\n" +
182 "The authority stays a contract-identity ContractAuthority; only the asserted principal changes." ,
183 newContractPath,
184 )
185
186 return dao.NewProposalRequest(title, description, dao.NewSimpleExecutor( 0 , cur, cb, "" ))
187 }
188
189 // NewInstructionsProposalCallback was removed, not deprecated: it was the
190 // capability leak described above, so an inert shim would be pointless and
191 // a working one would reopen the hole. Precedent + replay check: the
192 // min-fee callback below was already removed the same way even though it
193 // HAD been used on gnoland1 (GovDAO Prop #19), whereas the instructions
194 // callback has no on-chain trace at all — the live realm's instructions
195 // are byte-identical to the genesis default and none of gnoland1's 26
196 // GovDAO proposals is an "Update instructions" one.
197 //
198 // The min-fee callback was removed: the fee now lives in sysparams
199 // under node:valoper:register_fee, and
200 // proposal.ProposeNewMinFeeProposalRequest delegates to
201 // sys/params.NewSysParamUint64PropRequest. Removing avoids the
202 // forward-compat hazard of a no-op shim with no caller-auth gating.
203
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.