proxy.gno
4.95 Kb ยท 195 lines
1package dao
2
3import (
4 "chain"
5 "chain/runtime"
6 "errors"
7 "strconv"
8
9 "gno.land/p/nt/ufmt"
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 if dao == nil {
53 return false
54 }
55 execute, err := dao.PreExecuteProposal(pid)
56 if err != nil {
57 panic(err.Error())
58 }
59
60 if !execute {
61 return false
62 }
63 prop, err := GetProposal(cur, pid)
64 if err != nil {
65 panic(err.Error())
66 }
67 if err := prop.executor.Execute(cross); err != nil {
68 panic(err.Error())
69 }
70 return true
71}
72
73// CreateProposal will try to create a new proposal, that will be validated by the actual
74// govDAO implementation. If the proposal cannot be created, an error will be returned.
75func CreateProposal(cur realm, r ProposalRequest) (ProposalID, error) {
76 if dao == nil {
77 return -1, errors.New("DAO not initialized")
78 }
79 author, err := dao.PreCreateProposal(r)
80 if err != nil {
81 return -1, err
82 }
83
84 p := &Proposal{
85 author: author,
86 title: r.title,
87 description: r.description,
88 executor: r.executor,
89 allowedDAOs: allowedDAOs[:],
90 }
91
92 pid := proposals.SetProposal(p)
93 dao.PostCreateProposal(r, pid)
94
95 chain.Emit("ProposalCreated",
96 "id", strconv.FormatInt(int64(pid), 10),
97 )
98
99 return pid, nil
100}
101
102func MustVoteOnProposal(cur realm, r VoteRequest) {
103 if err := VoteOnProposal(cur, r); err != nil {
104 panic(err.Error())
105 }
106}
107
108// VoteOnProposal sends a vote to the actual govDAO implementation.
109// If the voter cannot vote the specified proposal, this method will return an error
110// with the explanation of why.
111func VoteOnProposal(cur realm, r VoteRequest) error {
112 if dao == nil {
113 return errors.New("DAO not initialized")
114 }
115 return dao.VoteOnProposal(r)
116}
117
118// MustVoteOnProposalSimple is like MustVoteOnProposal but intended to be used through gnokey with basic types.
119func MustVoteOnProposalSimple(cur realm, pid int64, option string) {
120 MustVoteOnProposal(cur, VoteRequest{
121 Option: VoteOption(option),
122 ProposalID: ProposalID(pid),
123 })
124}
125
126func MustGetProposal(cur realm, pid ProposalID) *Proposal {
127 p, err := GetProposal(cur, pid)
128 if err != nil {
129 panic(err.Error())
130 }
131
132 return p
133}
134
135// GetProposal gets created proposal by its ID
136func GetProposal(cur realm, pid ProposalID) (*Proposal, error) {
137 if dao == nil {
138 return nil, errors.New("DAO not initialized")
139 }
140 if err := dao.PreGetProposal(pid); err != nil {
141 return nil, err
142 }
143
144 prop := proposals.GetProposal(pid)
145 if prop == nil {
146 return nil, errors.New(ufmt.Sprintf("Proposal %v does not exist.", int64(pid)))
147 }
148
149 if err := dao.PostGetProposal(pid, prop); err != nil {
150 return nil, err
151 }
152
153 return prop, nil
154}
155
156// UpdateImpl is a method intended to be used on a proposal.
157// This method will update the current govDAO implementation
158// to a new one. AllowedDAOs are a list of realms that can
159// call this method, in case the new DAO implementation had
160// a breaking bug. Any value set as nil will be ignored.
161// If AllowedDAOs field is not set correctly, the actual DAO
162// implementation wont be able to execute new Proposals!
163func UpdateImpl(cur realm, r UpdateRequest) {
164 gRealm := runtime.PreviousRealm().PkgPath()
165
166 if !InAllowedDAOs(gRealm) {
167 panic("permission denied for prev realm: " + gRealm)
168 }
169
170 if r.AllowedDAOs != nil {
171 allowedDAOs = r.AllowedDAOs
172 }
173
174 if r.DAO != nil {
175 dao = r.DAO
176 }
177}
178
179func AllowedDAOs() []string {
180 dup := make([]string, len(allowedDAOs))
181 copy(dup, allowedDAOs)
182 return dup
183}
184
185func InAllowedDAOs(pkg string) bool {
186 if len(allowedDAOs) == 0 {
187 return true // corner case for initialization
188 }
189 for _, d := range allowedDAOs {
190 if pkg == d {
191 return true
192 }
193 }
194 return false
195}