PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/sys/params

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
params
Namespace
sys
Files
14 (gnomod.toml)
Exported functions
33
Module
gno.land/r/sys/params
gno
0.9

Files (14)

  • gnomod.tomltoml
  • delegate.gnogno
  • fee_collector.gnogno
  • halt.gnogno
  • params.gnogno
  • render.gnogno
  • run_submitters.gnogno
  • unlock.gnogno
  • valoper.gnogno
  • valset.gnogno
  • delegate_test.gnogno
  • fee_collector_test.gnogno
  • params_test.gnogno
  • unlock_test.gnogno
  • run_submitters.gnogno
    1package params23// Delegated management of vm:p:run_submitters, the allowlist of addresses4// permitted to send MsgRun.5//6// See delegate.gno for why this is a named slot rather than a registry.7//8

    Functions

    • AddRunSubmitters(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}, addrs []string)

    • GetRunSubmitters() []string

    • GetValoperRegisterFee() uint64

    • GetValoperRotationFee() uint64

    • GetValoperRotationPeriodBlocks() int64

    • GetValsetEffective() []gno.land/p/sys/validators/v0.Validator

    • GetValsetEntries() []gno.land/p/sys/validators/v0.Validator

    • GetValsetPubKeyTypes() []string

    • IsRunSubmittersDelegate(pkgpath string) bool

    • NewSetFeeCollectorRequest(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) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSetHaltRequest(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}, height int64, minVersion string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamBoolPropRequest(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}, module string, submodule string, name string, value bool) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamBytesPropRequest(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}, module string, submodule string, name string, value []uint8) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamInt64PropRequest(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}, module string, submodule string, name string, value int64) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamStringPropRequest(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}, module string, submodule string, name string, value string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamStringsPropRequest(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}, module string, submodule string, name string, value []string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamStringsPropRequestAddWithTitle(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}, module string, submodule string, name string, title string, value []string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamStringsPropRequestRemoveWithTitle(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}, module string, submodule string, name string, title string, value []string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamStringsPropRequestWithTitle(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}, module string, submodule string, name string, title string, value []string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewSysParamUint64PropRequest(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}, module string, submodule string, name string, value uint64) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • ProposeAddUnrestrictedAcctsRequest(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}, addrs ....uverse.address) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • ProposeClearRunSubmittersManager(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}) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • ProposeLockTransferRequest(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}) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • ProposeRemoveUnrestrictedAcctsRequest(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}, addrs ....uverse.address) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • ProposeSetRunSubmitters(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}, addrs []string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • ProposeSetRunSubmittersManager(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}, pkgpath string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • ProposeUnlockTransferRequest(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}) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • RemoveRunSubmitters(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}, addrs []string)

    • Render(path string) string

    • RunSubmittersGrantedBy(addr string) bool

    • RunSubmittersManager() string

    • SetValsetProposal(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}, entries []string)

    • ValsetDirty() bool

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    Rendered

    RenderedRawgnoweb ↗
    // What the delegate can and cannot do, and why the asymmetry is this way round:
    9//
    10// - It may ADD addresses. This is the routine work the delegation exists for.
    11// - It may REMOVE only addresses it added itself, and never the last one.
    12// De-listing its own mistake is a core part of managing an allowlist, so
    13// add-only would be a strange capability to hand out. Two bounds keep that
    14// safe. Grant-scoping stops it removing an address that predates the
    15// delegation. The non-empty floor in RemoveRunSubmitters stops it reaching
    16// zero by any route — an empty list means the gate is OFF and anyone may
    17// MsgRun, so emptying it would let the delegate revoke the entire
    18// restriction GovDAO voted for, which is the one thing this capability must
    19// not be able to do.
    20// - GovDAO retains everything, through ProposeSetRunSubmitters below -- the
    21// generic factories no longer accept this key. That is the bounded reset:
    22// one proposal returns the key to a known-good list regardless of what the
    23// delegate did, and the proposal shows voters the exact resulting list.
    24//
    25// A cost to state plainly rather than bury: this key is read by the ante handler
    26// on EVERY transaction, before the per-tx gas meter exists, so its length is an
    27// unmetered per-transaction constant for the whole chain. The delegate therefore
    28// holds a knob on that constant, bounded only by maxAddressListLen in
    29// gno.land/pkg/sdk/vm/params.go. That bound is enforced on this path for free:
    30// UpdateSysParamStrings re-sets the whole list, which re-enters WillSetParam and
    31// Params.Validate, so both the cap and bech32 validation apply to a delegate's
    32// additions. A key that is not read on the ante path would be a cheaper first
    33// delegation; this one is the one that was asked for.
    34
    35import (
    36 "chain"
    37 prms "sys/params"
    38
    39 "gno.land/p/moul/addrset/v0"
    40
    41 "gno.land/r/gov/dao"
    42)
    43
    44const (
    45 vmModulePrefix = "vm"
    46 vmParamsSubmodule = "p"
    47
    48 runSubmittersKey = "run_submitters"
    49)
    50
    51// runSubmittersMgr is the package path authorized to manage run_submitters.
    52// Empty means the capability is not delegated, and empty must deny — see
    53// assertDelegate.
    54var runSubmittersMgr string
    55
    56// runSubmittersGrants records which addresses the current delegate added, so
    57// removal can be scoped to its own grants.
    58//
    59// The parameter is the source of truth and this is a side table, so the two can
    60// disagree — genesis, `gnogenesis params set`, or any future direct keeper write
    61// produces entries with no grant recorded. That direction is safe: an unrecorded
    62// address is simply not removable by the delegate, which is the conservative
    63// answer. Cleared whenever the delegation changes hands, so a new delegate never
    64// inherits authority over its predecessor's grants.
    65var runSubmittersGrants = addrset.Set{}
    66
    67// RunSubmittersManager returns the package path currently authorized to manage
    68// run_submitters, or "" when the capability is not delegated.
    69func RunSubmittersManager() string {
    70 return runSubmittersMgr
    71}
    72
    73// RunSubmittersGrantedBy reports whether the current delegate added addr, i.e.
    74// whether it may remove it.
    75//
    76// Exposed so a delegate can check before acting. A delegate that discovers a
    77// refusal by panicking mid-proposal-execution is in a bad place: the panic
    78// aborts the transaction, and for a DAO whose proposal has already passed, every
    79// retry aborts the same way.
    80func RunSubmittersGrantedBy(addr address) bool {
    81 return runSubmittersGrants.Has(addr)
    82}
    83
    84// IsRunSubmittersDelegate reports whether pkgpath currently holds the
    85// capability. Pure predicate, for a caller that wants to fail cleanly rather
    86// than be panicked at.
    87func IsRunSubmittersDelegate(pkgpath string) bool {
    88 return pkgpath != "" && pkgpath == runSubmittersMgr
    89}
    90
    91// ProposeSetRunSubmittersManager creates a GovDAO proposal handing management of
    92// run_submitters to pkgpath.
    93//
    94// pkgpath may be a sub-realm identity such as
    95// "gno.land/r/nt/commondao/v0#dao/42", which is how a single DAO hosted by a
    96// multi-tenant realm is named. Matching is exact, so naming the bare host would
    97// authorize the host itself and none of its DAOs.
    98func ProposeSetRunSubmittersManager(cur realm, pkgpath string) dao.ProposalRequest {
    99 assertDelegatePath(pkgpath)
    100 if pkgpath == runSubmittersMgr {
    101 panic("no-op proposal rejected: " + pkgpath + " already manages " + runSubmittersKey)
    102 }
    103
    104 // desc uses the manager as of proposal creation, which is the honest thing
    105 // to show a voter. The EVENT reads it again inside the executor, because two
    106 // proposals created while the slot is empty and executed in sequence would
    107 // otherwise both report from="" while the second actually replaced the first.
    108 from := runSubmittersMgr
    109 desc := "Authorize " + pkgpath + " to add addresses to the " + runSubmittersKey +
    110 " allowlist, which gates who may send MsgRun. It may remove only " +
    111 "addresses it added itself. GovDAO retains full control, including " +
    112 "replacing the whole list."
    113 if from != "" {
    114 desc += " This replaces the current manager, " + from +
    115 ", and discards the record of which addresses it granted."
    116 }
    117
    118 return newDelegateProposal(cur, DelegateSetEvent, runSubmittersKey, pkgpath,
    119 "Delegate "+runSubmittersKey+" management", desc,
    120 func() string {
    121 prev := runSubmittersMgr
    122 runSubmittersMgr = pkgpath
    123 // A new holder must not inherit removal authority over addresses
    124 // the previous one granted.
    125 runSubmittersGrants = addrset.Set{}
    126 return prev
    127 })
    128}
    129
    130// ProposeClearRunSubmittersManager creates a GovDAO proposal revoking the
    131// delegation.
    132//
    133// Revocation is immediate on execution because the slot is consulted on every
    134// call. It deliberately does NOT remove addresses the delegate added: sweeping
    135// them would make the executed effect invisible at vote time, and would silently
    136// remove nothing whenever the grant record had drifted. Use the existing
    137// whole-list setter to reset the list to a reviewed value.
    138func ProposeClearRunSubmittersManager(cur realm) dao.ProposalRequest {
    139 if runSubmittersMgr == "" {
    140 panic("no-op proposal rejected: " + runSubmittersKey + " is not delegated")
    141 }
    142
    143 from := runSubmittersMgr
    144 return newDelegateProposal(cur, DelegateClearedEvent, runSubmittersKey, "",
    145 "Revoke "+runSubmittersKey+" management",
    146 "Revoke "+from+"'s authority to manage the "+runSubmittersKey+
    147 " allowlist. Addresses it already added REMAIN on the list; reset the "+
    148 "list explicitly if that is not wanted.",
    149 func() string {
    150 prev := runSubmittersMgr
    151 runSubmittersMgr = ""
    152 runSubmittersGrants = addrset.Set{}
    153 return prev
    154 })
    155}
    156
    157// AddRunSubmitters adds addresses to the run_submitters allowlist.
    158//
    159// Callable only by the delegated manager. Addresses already present are a no-op
    160// (UpdateSysParamStrings dedupes), and the chain still validates every entry and
    161// enforces the list-length cap, because the update re-sets the whole list.
    162func AddRunSubmitters(cur realm, addrs []string) {
    163 assertDelegate(0, cur, runSubmittersMgr, "the "+runSubmittersKey+" allowlist")
    164 if len(addrs) == 0 {
    165 return
    166 }
    167
    168 // The delegate may curate a list that is already in force. It may not put
    169 // one into force.
    170 //
    171 // An empty run_submitters means the allowlist is OFF and anyone may MsgRun.
    172 // So the first add is not curation: it switches a chain-wide restriction on
    173 // and picks who it admits. A delegate adding one address to an empty list
    174 // leaves that address the only one on the chain that may run code.
    175 //
    176 // Refused rather than discouraged because it is unrepairable. Creating a
    177 // GovDAO proposal needs MsgRun -- a ProposalRequest carries an Executor,
    178 // which MsgCall cannot build from string arguments -- so once the gate is
    179 // armed against the members they cannot propose the vote that would undo
    180 // it, and the floor in RemoveRunSubmitters stops the delegate undoing it
    181 // either.
    182 if len(GetRunSubmitters()) == 0 {
    183 panic("refusing to arm the " + runSubmittersKey +
    184 " allowlist: it is empty, so the gate is off and anyone may MsgRun. " +
    185 "Turning it on is a GovDAO vote, not a delegated edit")
    186 }
    187
    188 // Record a grant only for an address this call actually ADDED.
    189 //
    190 // UpdateSysParamStrings dedupes on add, so passing an address already on the
    191 // list leaves the parameter unchanged. Recording a grant for it anyway would
    192 // let the delegate launder authority over entries it never granted: read the
    193 // list, re-add all of it (a no-op on the parameter, but every address now
    194 // recorded as its own), then remove all of it -- including whatever predated
    195 // the delegation. The floor in RemoveRunSubmitters would refuse the last of
    196 // those removals, but only the last: everything up to it would still go
    197 // through, leaving the delegate holding the only listed address and so the
    198 // sole authority over who may MsgRun.
    199 //
    200 // So the grant record has to follow the parameter, not the argument.
    201 present := make(map[string]bool)
    202 for _, a := range GetRunSubmitters() {
    203 present[a] = true
    204 }
    205
    206 prms.UpdateSysParamStrings(vmModulePrefix, vmParamsSubmodule, runSubmittersKey, addrs, true)
    207 for _, a := range addrs {
    208 if !present[a] {
    209 runSubmittersGrants.Add(address(a))
    210 }
    211 }
    212 chain.Emit(DelegateWriteEvent,
    213 "key", runSubmittersKey, "realm", runSubmittersMgr, "op", "add")
    214}
    215
    216// RemoveRunSubmitters removes addresses from the run_submitters allowlist.
    217//
    218// Callable only by the delegated manager, and only for addresses that manager
    219// added. Refusing rather than silently skipping is deliberate: a partial removal
    220// that reported success would leave the caller believing an address was
    221// de-listed when it was not.
    222func RemoveRunSubmitters(cur realm, addrs []string) {
    223 assertDelegate(0, cur, runSubmittersMgr, "the "+runSubmittersKey+" allowlist")
    224 if len(addrs) == 0 {
    225 return
    226 }
    227 for _, a := range addrs {
    228 if !runSubmittersGrants.Has(address(a)) {
    229 panic("cannot remove " + a + ": not granted by " + runSubmittersMgr +
    230 ", only GovDAO may remove it")
    231 }
    232 }
    233
    234 // The delegate may not empty the list, whatever it granted.
    235 //
    236 // An empty run_submitters means the gate is OFF -- anyone on the chain may
    237 // send MsgRun. So emptying it is not a smaller version of removing one
    238 // address, it is the opposite of what the delegation is for: it would let a
    239 // delegate authorized to curate a list unilaterally revoke the whole
    240 // restriction GovDAO voted for.
    241 //
    242 // Grant-scoping alone does not prevent this. It holds only while at least
    243 // one entry the delegate did not grant survives, and GovDAO replacing the
    244 // list wholesale can remove its own entries without touching the grant
    245 // record. A floor makes the invariant structural instead of emergent.
    246 //
    247 // Counted against the parameter, not the argument: the caller may name
    248 // addresses that are not listed, or name one twice, and neither shrinks the
    249 // list. Only GovDAO can go to zero, through the whole-list setter, where the
    250 // resulting list is on the ballot.
    251 removing := make(map[string]bool, len(addrs))
    252 for _, a := range addrs {
    253 removing[a] = true
    254 }
    255 remaining := 0
    256 for _, a := range GetRunSubmitters() {
    257 if !removing[a] {
    258 remaining++
    259 }
    260 }
    261 if remaining == 0 {
    262 panic("refusing to empty the " + runSubmittersKey +
    263 " allowlist: an empty list disables the gate entirely, so only " +
    264 "GovDAO may do it")
    265 }
    266
    267 prms.UpdateSysParamStrings(vmModulePrefix, vmParamsSubmodule, runSubmittersKey, addrs, false)
    268 for _, a := range addrs {
    269 runSubmittersGrants.Remove(address(a))
    270 }
    271 chain.Emit(DelegateWriteEvent,
    272 "key", runSubmittersKey, "realm", runSubmittersMgr, "op", "remove")
    273}
    274
    275// GetRunSubmitters returns the current allowlist.
    276func GetRunSubmitters() []string {
    277 vals, _ := prms.GetSysParamStrings(vmModulePrefix, vmParamsSubmodule, runSubmittersKey)
    278 return vals
    279}
    280
    281// ProposeSetRunSubmitters creates a GovDAO proposal replacing the whole
    282// run_submitters allowlist.
    283//
    284// This is the only way to set the list by vote: the generic factories refuse
    285// the key (see assertNotRunSubmittersKey), so every whole-list write comes
    286// through here and carries the rule below.
    287//
    288// The proposer must be on the list they propose.
    289//
    290// An empty run_submitters means the gate is off and anyone may MsgRun. A
    291// non-empty one therefore decides who may run code at all -- and a list naming
    292// nobody who can create a GovDAO proposal cannot be undone, because creating a
    293// proposal needs MsgRun: a ProposalRequest carries an Executor, and MsgCall
    294// cannot build one from string arguments. The vote would end governance.
    295//
    296// Requiring the proposer's own address is a cheap way to prove the list is
    297// usable rather than merely plausible. GovDAO refuses a proposal from a
    298// non-member (PreCreateProposal, "only members can create new proposals"), so
    299// if this proposal exists at all its author is a member -- and they just signed
    300// the transaction that created it, so the address demonstrably holds a key. A
    301// list that merely NAMES a member proves neither: the address may belong to
    302// nobody, since any member can enroll an arbitrary address.
    303//
    304// Checked here, at proposal creation, rather than inside the executor. Nothing
    305// in r/gov/dao recovers from an executor panic, so a check that fires at
    306// execution turns a passed proposal into one that can never be executed. Here
    307// the refusal reaches a person who can still fix the list and propose again.
    308//
    309// This is a floor, not an invariant. The proposer may resign from GovDAO later,
    310// and the list is not re-checked when they do. It rules out arriving at a dead
    311// list in one vote; it cannot rule out drifting into one.
    312func ProposeSetRunSubmitters(cur realm, addrs []string) dao.ProposalRequest {
    313 // IsCurrent before Previous, as AGENTS.md requires and assertDelegate does.
    314 //
    315 // Belt and braces here rather than load-bearing: the compiler already
    316 // refuses anything but `cur` or `cross(rlm)` as the first argument to a
    317 // crossing function, so this cur is live by construction and the check
    318 // cannot fire. It earns its place if this ever becomes a helper taking an
    319 // ordinary realm parameter, which is the shape assertDelegate has and where
    320 // a stashed value really can be threaded in.
    321 if !cur.IsCurrent() {
    322 panic("unauthorized: cur is not the caller's live realm")
    323 }
    324 proposer := cur.Previous().Address()
    325 listed := false
    326 for _, a := range addrs {
    327 if a == proposer.String() {
    328 listed = true
    329 break
    330 }
    331 }
    332 if len(addrs) > 0 && !listed {
    333 panic("refusing to propose a " + runSubmittersKey + " allowlist that omits " +
    334 "the proposer " + proposer.String() + ": a non-empty list that names nobody " +
    335 "who can create a proposal cannot be changed back")
    336 }
    337
    338 title := "Set the " + runSubmittersKey + " allowlist"
    339 desc := "Replace the " + runSubmittersKey + " allowlist, which gates who may " +
    340 "send MsgRun."
    341 if len(addrs) == 0 {
    342 desc += " This empties the list, which switches the gate OFF: anyone may " +
    343 "send MsgRun."
    344 } else {
    345 desc += " Only these addresses may send MsgRun. Creating a GovDAO proposal " +
    346 "needs MsgRun, so this list also decides who can govern."
    347 for _, a := range addrs {
    348 desc += "\n- " + a
    349 }
    350 }
    351
    352 callback := func(cur realm) error {
    353 setRunSubmitters(addrs)
    354 chain.Emit("SetRunSubmitters", "key", runSubmittersKey, "proposer", proposer.String())
    355 return nil
    356 }
    357 return dao.NewProposalRequest(title, desc,
    358 dao.NewSimpleExecutor(0, cur, callback, ""))
    359}
    360
    361// setRunSubmitters replaces the whole allowlist.
    362//
    363// SetSysParamStrings, not UpdateSysParamStrings: Update with add=true appends
    364// non-duplicates onto what is already there, so it can never remove an address
    365// or empty the list. This realm reserves the key from the generic factories, so
    366// this is the only route by vote -- if it appended, the parameter would be
    367// append-only chain-wide, a compromised address could never be de-listed, and
    368// the gate could never be turned back off.
    369//
    370// A named function rather than the executor's body inline, so a test can reach
    371// it: a closure held in a ProposalRequest cannot be called from outside.
    372func setRunSubmitters(addrs []string) {
    373 prms.SetSysParamStrings(vmModulePrefix, vmParamsSubmodule, runSubmittersKey, addrs)
    374}
    375

    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.