Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}