Search Apps Documentation Source Content File Folder Download Copy Actions Download

proxy.gno

5.82 Kb · 217 lines
  1package dao
  2
  3import (
  4	"chain"
  5	"chain/runtime"
  6	"errors"
  7	"strconv"
  8
  9	"gno.land/p/nt/ufmt/v0"
 10)
 11
 12// dao is the actual govDAO implementation, having all the needed business logic
 13var dao DAO
 14
 15// allowedDAOs contains realms that can be used to update the actual govDAO implementation,
 16// and validate Proposals.
 17// This is like that to be able to rollback using a previous govDAO implementation in case
 18// the latest implementation has a breaking bug. After a test period, a proposal can be
 19// executed to remove all previous govDAOs implementations and leave the last one.
 20var allowedDAOs []string
 21
 22// proposals contains all the proposals in history.
 23var proposals *Proposals = NewProposals()
 24
 25// Remember this realm for rendering.
 26var gRealm = runtime.CurrentRealm()
 27
 28// Render calls directly to Render's DAO implementation.
 29// This allows to have this realm as the main entry point for everything.
 30func Render(p string) string {
 31	if dao == nil {
 32		return "DAO not initialized"
 33	}
 34	return dao.Render(gRealm.PkgPath(), p)
 35}
 36
 37// MustCreateProposal is an utility method that does the same as CreateProposal,
 38// but instead of erroing if something happens, it panics.
 39func MustCreateProposal(cur realm, r ProposalRequest) ProposalID {
 40	pid, err := CreateProposal(cur, r)
 41	if err != nil {
 42		panic(err.Error())
 43	}
 44
 45	return pid
 46}
 47
 48// ExecuteProposal will try to execute the proposal with the provided ProposalID.
 49// If the proposal was denied, it will return false. If the proposal is correctly
 50// executed, it will return true. If something happens this function will panic.
 51func ExecuteProposal(cur realm, pid ProposalID) bool {
 52	return executeProposal(cur, pid, false)
 53}
 54
 55// ExecuteOrRejectProposal executes the proposal with the provided ProposalID or rejects
 56// it when there is an execution error.
 57// If the proposal was denied, it will return false. If the proposal is correctly
 58// executed, it will return true, unless execution fails with an error, in which case
 59// proposal is rejected with the error as the reason.
 60// This function allows to finish proposals by rejecting them when there is a state
 61// change or an error in the proposal parameters that makes execution fail, potentially
 62// leaving the proposal active forever because it can't be successfully executed.
 63func ExecuteOrRejectProposal(cur realm, pid ProposalID) bool {
 64	return executeProposal(cur, pid, true)
 65}
 66
 67// CreateProposal will try to create a new proposal, that will be validated by the actual
 68// govDAO implementation. If the proposal cannot be created, an error will be returned.
 69func CreateProposal(cur realm, r ProposalRequest) (ProposalID, error) {
 70	if dao == nil {
 71		return -1, errors.New("DAO not initialized")
 72	}
 73	author, err := dao.PreCreateProposal(r)
 74	if err != nil {
 75		return -1, err
 76	}
 77
 78	p := &Proposal{
 79		author:      author,
 80		title:       r.title,
 81		description: r.description,
 82		executor:    r.executor,
 83		allowedDAOs: allowedDAOs[:],
 84	}
 85
 86	pid := proposals.SetProposal(p)
 87	dao.PostCreateProposal(r, pid)
 88
 89	chain.Emit("ProposalCreated",
 90		"id", strconv.FormatInt(int64(pid), 10),
 91	)
 92
 93	return pid, nil
 94}
 95
 96func MustVoteOnProposal(cur realm, r VoteRequest) {
 97	if err := VoteOnProposal(cur, r); err != nil {
 98		panic(err.Error())
 99	}
100}
101
102// VoteOnProposal sends a vote to the actual govDAO implementation.
103// If the voter cannot vote the specified proposal, this method will return an error
104// with the explanation of why.
105func VoteOnProposal(cur realm, r VoteRequest) error {
106	if dao == nil {
107		return errors.New("DAO not initialized")
108	}
109	return dao.VoteOnProposal(r)
110}
111
112// MustVoteOnProposalSimple is like MustVoteOnProposal but intended to be used through gnokey with basic types.
113func MustVoteOnProposalSimple(cur realm, pid int64, option string) {
114	MustVoteOnProposal(cur, VoteRequest{
115		Option:     VoteOption(option),
116		ProposalID: ProposalID(pid),
117	})
118}
119
120func MustGetProposal(cur realm, pid ProposalID) *Proposal {
121	p, err := GetProposal(cur, pid)
122	if err != nil {
123		panic(err.Error())
124	}
125
126	return p
127}
128
129// GetProposal gets created proposal by its ID
130func GetProposal(cur realm, pid ProposalID) (*Proposal, error) {
131	if dao == nil {
132		return nil, errors.New("DAO not initialized")
133	}
134	if err := dao.PreGetProposal(pid); err != nil {
135		return nil, err
136	}
137
138	prop := proposals.GetProposal(pid)
139	if prop == nil {
140		return nil, errors.New(ufmt.Sprintf("Proposal %v does not exist.", int64(pid)))
141	}
142
143	if err := dao.PostGetProposal(pid, prop); err != nil {
144		return nil, err
145	}
146
147	return prop, nil
148}
149
150// UpdateImpl is a method intended to be used on a proposal.
151// This method will update the current govDAO implementation
152// to a new one. AllowedDAOs are a list of realms that can
153// call this method, in case the new DAO implementation had
154// a breaking bug. Any value set as nil will be ignored.
155// If AllowedDAOs field is not set correctly, the actual DAO
156// implementation wont be able to execute new Proposals!
157func UpdateImpl(cur realm, r UpdateRequest) {
158	gRealm := runtime.PreviousRealm().PkgPath()
159
160	if !InAllowedDAOs(gRealm) {
161		panic("permission denied for prev realm: " + gRealm)
162	}
163
164	if r.AllowedDAOs != nil {
165		allowedDAOs = r.AllowedDAOs
166	}
167
168	if r.DAO != nil {
169		dao = r.DAO
170	}
171}
172
173func AllowedDAOs() []string {
174	dup := make([]string, len(allowedDAOs))
175	copy(dup, allowedDAOs)
176	return dup
177}
178
179func InAllowedDAOs(pkg string) bool {
180	if len(allowedDAOs) == 0 {
181		return true // corner case for initialization
182	}
183	for _, d := range allowedDAOs {
184		if pkg == d {
185			return true
186		}
187	}
188	return false
189}
190
191func executeProposal(cur realm, pid ProposalID, execErrorRejects bool) bool {
192	if dao == nil {
193		return false
194	}
195	execute, err := dao.PreExecuteProposal(pid)
196	if err != nil {
197		panic(err.Error())
198	}
199
200	if !execute {
201		return false
202	}
203	prop, err := GetProposal(cur, pid)
204	if err != nil {
205		panic(err.Error())
206	}
207
208	err = dao.ExecuteProposal(pid, prop.executor)
209	if err != nil {
210		if execErrorRejects {
211			return false
212		}
213
214		panic(err.Error())
215	}
216	return true
217}