PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/gov/dao

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
dao
Namespace
gov
Files
5 (gnomod.toml)
Exported functions
21
Module
gno.land/r/gov/dao
gno
0.9

Files (5)

  • gnomod.tomltoml
  • proxy.gnogno
  • types.gnogno
  • allowlist_test.gnogno
  • proxy_test.gnogno
types.gnogno
1package dao23import (4	"errors"5	"strings"67	"gno.land/p/nt/bptree/v0"8	"gno.land/p/nt/seqid/v0"9)1011type ProposalID int641213func (pid ProposalID) String() string {14	return seqid.ID(pid).String()15}1617// VoteOption is the limited voting option for a DAO proposal18// New govDAOs can create their own VoteOptions if needed in the19// future.20type VoteOption string2122const (23	AbstainVote VoteOption = "ABSTAIN" // Side is not chosen24	YesVote     VoteOption = "YES"     // Proposal should be accepted25	NoVote      VoteOption = "NO"      // Proposal should be rejected26)2728type VoteRequest struct {29	Option     VoteOption30	ProposalID ProposalID31	Metadata   interface{}32}3334func NewVoteRequest(option VoteOption, proposalID ProposalID) VoteRequest {35	return VoteRequest{36		Option:     option,37		ProposalID: proposalID,38	}39}4041func NewVoteRequestWithMetadata(option VoteOption, proposalID ProposalID, metadata interface{}) VoteRequest {42	return VoteRequest{43		Option:     option,44		ProposalID: proposalID,45		Metadata:   metadata,46	}47}4849func NewProposalRequest(title string, description string, executor Executor) ProposalRequest {50	return ProposalRequest{51		title:       title,52		description: description,53		executor:    executor,54	}55}5657func NewProposalRequestWithFilter(title string, description string, executor Executor, filter Filter) ProposalRequest {58	return ProposalRequest{59		title:       title,60		description: description,61		executor:    executor,62		filter:      filter,63	}64}6566type Filter interface{}6768type ProposalRequest struct {69	title       string70	description string71	executor    Executor72	filter      Filter73}7475func (p *ProposalRequest) Title() string {76	return p.title77}7879func (p *ProposalRequest) Description() string {80	return p.description81}8283func (p *ProposalRequest) Filter() Filter {84	return p.filter85}8687type Proposal struct {88	author address8990	title       string91	description string9293	executor    Executor94	allowedDAOs []string95}9697func (p *Proposal) Author() address {98	return p.author99}100101func (p *Proposal) Title() string {102	return p.title103}104105func (p *Proposal) Description() string {106	return p.description107}108109func (p *Proposal) ExecutorString() string {110	if p.executor != nil {111		return p.executor.String()112	}113114	return ""115}116117func (p *Proposal) ExecutorCreationRealm() string {118	if p.executor != nil {119		return p.executor.CreationRealm()120	}121122	return ""123}124125func (p *Proposal) AllowedDAOs() []string {126	return append([]string(nil), p.allowedDAOs...)127}128129type Proposals struct {130	seq            seqid.ID131	*bptree.BPTree // *bptree.BPTree[ProposalID]*Proposal132}133134func NewProposals() *Proposals {135	return &Proposals{BPTree: bptree.NewBPTree32()}136}137138func (ps *Proposals) SetProposal(p *Proposal) ProposalID {139	pid := ProposalID(int64(ps.seq))140	updated := ps.Set(pid.String(), p)141	if updated {142		panic("fatal error: Override proposals is not allowed")143	}144	ps.seq = ps.seq.Next()145	return pid146}147148func (ps *Proposals) GetProposal(pid ProposalID) *Proposal {149	pv := ps.Get(pid.String())150	if pv == nil {151		return nil152	}153154	return pv.(*Proposal)155}156157type Executor interface {158	Execute(cur realm) error159	String() string160	CreationRealm() string161}162163// NewSimpleExecutor constructs an Executor whose creationRealm is captured164// from rlm.PkgPath() at construction time. The IsCurrent() check rejects165// stale or stashed realm values so the captured value is the authentic166// caller realm. creationRealm is display-only (rendered as "Executor167// created in: ..." in proposal listings) — no auth gate downstream.168func NewSimpleExecutor(_ int, rlm realm, callback func(realm) error, description string) *SimpleExecutor {169	if !rlm.IsCurrent() {170		panic("NewSimpleExecutor: rlm is not the caller's live cur (stale capture or sibling frame)")171	}172	if callback == nil {173		panic("executor callback must not be nil")174	}175176	return &SimpleExecutor{177		callback:      callback,178		desc:          description,179		creationRealm: rlm.PkgPath(),180	}181}182183// SimpleExecutor implements the Executor interface using184// a callback function and a description string.185type SimpleExecutor struct {186	callback      func(realm) error187	desc          string188	creationRealm string189}190191// proxyPkgPath is this package: the realm whose frame Execute mints for a192// callback, and therefore the only realm entitled to invoke one.193const proxyPkgPath = "gno.land/r/gov/dao"194195// Execute runs the proposal's callback. Invocable only from this proxy.196//197// SECURITY: Execute is a CROSSING method declared here, so invoking it mints198// a gno.land/r/gov/dao frame for the callback — `cur.Previous()` inside the199// callback is this path whoever called. That is a capability: realms gate on200// it (authz.NewContractAuthority("gno.land/r/gov/dao") + DoByPrevious,201// ownable, anything reading Previous().Address()). Ungated, any realm could202// wrap a reachable `func(realm) error` — including an ordinary exported203// entrypoint of the victim — and run it with governance's identity, with no204// proposal id minted, so nothing to render, audit or deny afterwards.205//206// NOT InAllowedDAOs: that is SafeExecutor's bug. It holds the impl path while207// an executor's caller is the proxy, so it rejects the approved route and208// fails open while empty. Exact path plus subpackages, because209// impl.ExecuteProposal is non-crossing (Previous() is the proxy exactly), so210// a bare prefix would reject every real proposal.211//212// Gates the executor object's INVOCATION only. Keeping a privileged closure,213// or an exported entrypoint shaped like `func(realm) error`, out of reach214// stays the consumer's job — see r/gnops/valopers/admin.gno215// and p/moul/authz's NewContractAuthority godoc.216func (e *SimpleExecutor) Execute(cur realm) error {217	// IsCurrent first, as every other gate here does: otherwise Previous() is218	// read from whatever realm value the caller threaded in.219	if !cur.IsCurrent() {220		return errors.New("execution denied: cur is not the caller's live realm")221	}222	if prev := cur.Previous().PkgPath(); prev != proxyPkgPath &&223		!strings.HasPrefix(prev, proxyPkgPath+"/") {224		return errors.New("execution denied: executors are only invocable by " + proxyPkgPath)225	}226227	// Check if executor was created using the constructor func228	if e.callback == nil {229		return nil230	}231232	return e.callback(cross(cur))233}234235func (e *SimpleExecutor) String() string {236	return e.desc237}238239func (e *SimpleExecutor) CreationRealm() string {240	return e.creationRealm241}242243func NewSafeExecutor(e Executor) *SafeExecutor {244	return &SafeExecutor{245		e: e,246	}247}248249// SafeExecutor wraps an Executor to only allow its execution250// by allowed govDAOs.251type SafeExecutor struct {252	e Executor253}254255func (e *SafeExecutor) Execute(cur realm) error {256	// IsCurrent first, matching every other allowlist gate in this tree257	// (proxy.go's UpdateImpl, treasury, memberstore). Without it this method258	// trusts whatever realm value it is handed, so a caller threading a stale259	// or sibling-frame cur would have its Previous() read from that value260	// rather than from the live frame.261	//262	// NewSafeExecutor has no call sites, so this type is currently dead code.263	// Live proposal execution goes through the Executor interface to264	// SimpleExecutor.Execute, which now gates on the invoking realm being the265	// proxy -- deliberately NOT on InAllowedDAOs, which holds the impl path266	// and therefore rejects the approved route and fails open while empty.267	// That is this method's bug; see SimpleExecutor.Execute.268	if !cur.IsCurrent() {269		return errors.New("execution denied: cur is not the caller's live realm")270	}271	// Verify the caller is an adequate Realm272	if !InAllowedDAOs(cur.Previous().PkgPath()) {273		return errors.New("execution only allowed by validated govDAOs")274	}275276	return e.e.Execute(cross(cur))277}278279func (e *SafeExecutor) String() string {280	return e.e.String()281}282283func (e *SafeExecutor) CreationRealm() string {284	return e.e.CreationRealm()285}286287// DAO is the govDAO implementation interface. All mutating/auth-gated288// methods take rlm as their realm-typed parameter in the second position289// (the `_ int, rlm realm` non-crossing form): callers thread the proxy's290// cur as data without forcing a realm transition, so the impl's existing291// unsafe.CurrentRealm()-based auth gates (isValidCall, memberstore.Get)292// continue to see the proxy realm. Render stays unchanged.293type DAO interface {294	// PreCreateProposal is called just before creating a new Proposal295	// It is intended to be used to get the address of the proposal, that296	// may vary depending on the DAO implementation, and to validate that297	// the requester is allowed to do a proposal298	PreCreateProposal(_ int, rlm realm, r ProposalRequest) (address, error)299300	// PostCreateProposal is called after creating the Proposal. It is301	// intended to be used as a way to store a new proposal status, that302	// depends on the actuall govDAO implementation303	PostCreateProposal(_ int, rlm realm, r ProposalRequest, pid ProposalID)304305	// VoteOnProposal will send a petition to vote for a specific proposal306	// to the actual govDAO implementation307	VoteOnProposal(_ int, rlm realm, r VoteRequest) error308309	// PreExecuteProposal is called when someone is trying to execute a proposal by ID.310	// Is intended to be used to validate who can trigger the proposal execution.311	PreExecuteProposal(_ int, rlm realm, pid ProposalID) (bool, error)312313	// ExecuteProposal executes the proposal executor and on error changes proposal314	// status to denied with the error message being the denial reason.315	// It returns the executor error when it fails.316	ExecuteProposal(_ int, rlm realm, pid ProposalID, e Executor) error317318	// Render will return a human-readable string in markdown format that319	// will be used to show new data through the dao proxy entrypoint.320	// Crossing: the chain query layer auto-injects .cur, and321	// implementations forward cur to internal rlm-aware helpers (mux322	// RenderRlm + downstream cross(rlm) reads).323	Render(cur realm, pkgpath string, path string) string324}325326type UpdateRequest struct {327	DAO         DAO328	AllowedDAOs []string329}330331// NewUpdateRequest copies allowedDAOs into a fresh slice owned by332// /r/gov/dao. Under the storage=authority model, if we stored the333// caller-passed slice directly, the base ArrayValue would retain334// PkgID = caller_realm: storage rent would attribute to caller, and335// /r/gov/dao could not mutate (e.g. append to) its own copy without336// a DidUpdate panic. The internal copy ensures the UpdateRequest337// and its AllowedDAOs both live entirely in /r/gov/dao's authority.338func NewUpdateRequest(d DAO, allowedDAOs []string) UpdateRequest {339	cp := make([]string, len(allowedDAOs))340	copy(cp, allowedDAOs)341	return UpdateRequest{342		DAO:         d,343		AllowedDAOs: cp,344	}345}346

Functions

  • AllowedDAOs() []string

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

  • ExecuteOrRejectProposal(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}, pid int64) bool

  • ExecuteProposal(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}, pid int64) bool

  • GetProposal(pid int64) (*gno.land/r/gov/dao.Proposal, interface {Error func() string})

  • InAllowedDAOs(pkg string) bool

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

  • MustGetProposal(pid int64) *gno.land/r/gov/dao.Proposal

  • MustVoteOnProposal(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}, r struct{Option gno.land/r/gov/dao.VoteOption; ProposalID gno.land/r/gov/dao.ProposalID; Metadata interface {}})

  • MustVoteOnProposalSimple(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}, pid int64, option string)

  • NewProposalRequest(title string, description string, executor interface {CreationRealm func() string; Execute func(.uverse.realm) .uverse.error; String func() string}) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

  • NewProposalRequestWithFilter(title string, description string, executor interface {CreationRealm func() string; Execute func(.uverse.realm) .uverse.error; String func() string}, filter interface {}) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

  • NewProposals() *gno.land/r/gov/dao.Proposals

  • NewSafeExecutor(e interface {CreationRealm func() string; Execute func(.uverse.realm) .uverse.error; String func() string}) *gno.land/r/gov/dao.SafeExecutor

  • NewSimpleExecutor(int, rlm 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}, callback func(.uverse.realm) .uverse.error, description string) *gno.land/r/gov/dao.SimpleExecutor

  • NewUpdateRequest(d interface {ExecuteProposal func(int, .uverse.realm, gno.land/r/gov/dao.ProposalID, gno.land/r/gov/dao.Executor) .uverse.error; PostCreateProposal func(int, .uverse.realm, gno.land/r/gov/dao.ProposalRequest, gno.land/r/gov/dao.ProposalID); PreCreateProposal func(int, .uverse.realm, gno.land/r/gov/dao.ProposalRequest) (.uverse.address, .uverse.error); PreExecuteProposal func(int, .uverse.realm, gno.land/r/gov/dao.ProposalID) (bool, .uverse.error); Render func(.uverse.realm, string, string) string; VoteOnProposal func(int, .uverse.realm, gno.land/r/gov/dao.VoteRequest) .uverse.error}, allowedDAOs []string) struct{DAO gno.land/r/gov/dao.DAO; AllowedDAOs []string}

  • NewVoteRequest(option string, proposalID int64) struct{Option gno.land/r/gov/dao.VoteOption; ProposalID gno.land/r/gov/dao.ProposalID; Metadata interface {}}

  • NewVoteRequestWithMetadata(option string, proposalID int64, metadata interface {}) struct{Option gno.land/r/gov/dao.VoteOption; ProposalID gno.land/r/gov/dao.ProposalID; Metadata interface {}}

  • Render(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}, p string) string

  • UpdateImpl(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}, r struct{DAO gno.land/r/gov/dao.DAO; AllowedDAOs []string})

  • VoteOnProposal(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}, r struct{Option gno.land/r/gov/dao.VoteOption; ProposalID gno.land/r/gov/dao.ProposalID; Metadata interface {}}) interface {Error func() string}

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

Rendered

RenderedRawgnoweb ↗

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.