Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

sharedpoll.gno

5.62 Kb · 206 lines
  1// Package sharedpoll is the Remix Lab shared-membership composition demo: a
  2// poll app whose electorate comes from an existing shared membership realm
  3// instead of its own member list.
  4//
  5// The shared realm is consulted read-only, when a poll opens, to snapshot
  6// its members. People join or leave through the shared realm's own
  7// interface. This app's administrators (who may create and open polls) are
  8// a separate, local set seeded with the deployer.
  9package sharedpoll
 10
 11import (
 12	"strings"
 13	"time"
 14
 15	"gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/ballots/v1"
 16	"gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/core/v1"
 17	"gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/membership/v1"
 18	"gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/policies/v1"
 19)
 20
 21const (
 22	pkgPath       = "gno.land/r/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/sharedpoll"
 23	recipeID      = "shared-poll-demo"
 24	recipeVersion = "1.0.0"
 25	pageSize      = 25
 26	optionSep     = "|"
 27	configDigest  = "unset"
 28)
 29
 30const (
 31	opAdmins = "admins.manage"
 32	opCreate = "poll.create"
 33	opOpen   = "poll.open"
 34	opPolicy = "policy.set"
 35)
 36
 37var (
 38	admins *membership.Set
 39	policy *policies.Policy
 40	polls  *ballots.Box
 41)
 42
 43func init(cur realm) {
 44	deployer := cur.Previous()
 45	if deployer.IsUser() && deployer.Address().IsValid() {
 46		setup(deployer.Address())
 47	}
 48}
 49
 50func setup(owner address) {
 51	admins = membership.New(membership.Config{
 52		JoinMode:   membership.JoinInvitation,
 53		MaxMembers: 10,
 54	}, owner)
 55	policy = policies.New(admins)
 56	policy.Define(opAdmins, policies.RuleOwner, policies.RuleOwner)
 57	policy.Define(opCreate, policies.RuleOwner, policies.RuleReviewer)
 58	policy.Define(opOpen, policies.RuleOwner, policies.RuleReviewer)
 59	policy.Define(opPolicy, policies.RuleOwner, policies.RuleOwner)
 60	polls = ballots.NewBox(ballots.Config{
 61		MaxBallots:     100,
 62		MaxOptions:     20,
 63		MaxElectorate:  100,
 64		QuorumPercent:  0,
 65		AllowAbstain:   false,
 66		MinDuration:    60,
 67		MaxDuration:    2592000,
 68		MaxTitleBytes:  140,
 69		MaxOptionBytes: 80,
 70	})
 71}
 72
 73// Descriptor identifies this app's recipe, components and bindings.
 74func Descriptor() core.AppDescriptor {
 75	return core.AppDescriptor{
 76		Recipe:        recipeID,
 77		RecipeVersion: recipeVersion,
 78		Components: []string{
 79			"[email protected]",
 80			"[email protected]",
 81			"[email protected]",
 82			"[email protected]",
 83			"[email protected]",
 84			"[email protected]",
 85		},
 86		ConfigDigest:      configDigest,
 87		ViewSchemaVersion: core.ViewSchemaVersion,
 88		Bindings:          []string{"membership.read/v1=" + sharedPath},
 89	}
 90}
 91
 92// AddAdmin grants addr a local administrative role ("reviewer" or "owner").
 93func AddAdmin(cur realm, addr address, role string) {
 94	authorize(cur, opAdmins)
 95	r, err := core.ParseRole(role)
 96	must(err)
 97	if r != core.RoleReviewer && r != core.RoleOwner {
 98		panic(core.Errorf(core.ErrInvalidInput, "admins are reviewers or owners").Error())
 99	}
100	must(admins.Add(addr, r))
101}
102
103// RemoveAdmin removes a local administrator.
104func RemoveAdmin(cur realm, addr address) {
105	authorize(cur, opAdmins)
106	must(admins.Remove(addr))
107}
108
109// CreatePoll creates a draft poll. options are separated by "|".
110func CreatePoll(cur realm, title string, options string, durationSeconds int64) uint64 {
111	actor := authorize(cur, opCreate)
112	id, err := polls.Create(actor, title, strings.Split(options, optionSep), durationSeconds)
113	must(err)
114	return id
115}
116
117// CancelPoll deletes a draft poll that was never opened. The poll's creator
118// may cancel it, and so may anyone allowed to open polls.
119func CancelPoll(cur realm, id uint64) {
120	ready()
121	actor := cur.Previous().Address()
122	if creator, ok := polls.Creator(id); ok && creator == actor {
123		must(policy.Authorize(opCreate, actor))
124	} else {
125		must(policy.Authorize(opOpen, actor))
126	}
127	must(polls.CancelDraft(id))
128}
129
130// OpenPoll snapshots the shared realm's current members as the electorate.
131func OpenPoll(cur realm, id uint64) {
132	authorize(cur, opOpen)
133	voters, ok := electorate.Snapshot(core.RoleMember, polls.Config().MaxElectorate)
134	if !ok {
135		panic(core.Errorf(core.ErrLimitExceeded, "electorate too large").Error())
136	}
137	must(polls.OpenWithElectorate(id, time.Now().Unix(), voters))
138}
139
140// Vote casts the caller's vote for option index choice.
141func Vote(cur realm, id uint64, choice int) {
142	ready()
143	must(polls.Vote(id, cur.Previous().Address(), choice, time.Now().Unix()))
144}
145
146// Finalize commits a poll's result once its deadline has passed.
147func Finalize(cur realm, id uint64) {
148	ready()
149	_, err := polls.Finalize(id, time.Now().Unix())
150	must(err)
151}
152
153// SetRule changes the rule for an operation, within its floor.
154func SetRule(cur realm, op string, rule string) {
155	authorize(cur, opPolicy)
156	r, err := policies.ParseRule(rule)
157	must(err)
158	must(policy.Set(op, r))
159}
160
161// Poll returns an inert summary of poll id.
162func Poll(id uint64) (core.BallotView, bool) {
163	ready()
164	return polls.Ballot(id)
165}
166
167// Polls pages through polls, newest first.
168func Polls(offset, limit int) []core.BallotView {
169	ready()
170	if limit < 1 || limit > pageSize {
171		limit = pageSize
172	}
173	return polls.List(offset, limit)
174}
175
176// AdminRole returns addr's local administrative role name ("none" when
177// addr is not an administrator).
178func AdminRole(addr address) string {
179	ready()
180	return admins.RoleOf(addr).String()
181}
182
183// HasVoted reports whether addr voted in poll id.
184func HasVoted(id uint64, addr address) bool {
185	ready()
186	return polls.HasVoted(id, addr)
187}
188
189func authorize(cur realm, op string) address {
190	ready()
191	actor := cur.Previous().Address()
192	must(policy.Authorize(op, actor))
193	return actor
194}
195
196func ready() {
197	if admins == nil {
198		panic(core.Errorf(core.ErrInvalidState, "app is not initialized").Error())
199	}
200}
201
202func must(err error) {
203	if err != nil {
204		panic(err.Error())
205	}
206}