organizer.gno
3.91 Kb · 127 lines
1package bolo
2
3import (
4 "chain"
5 "chain/banker"
6 "chain/runtime"
7 "math"
8)
9
10// Who hid a campaign's photo: ImageHidden's by (API §6).
11const (
12 hiderOrganizer = "organizer"
13 hiderModerator = "moderator"
14)
15
16// PostBond adds what you send to campaign id's bond: a stake its organizer may post,
17// lost if Bolo's moderator delists the campaign for fraud. It is not a guarantee. The
18// organizer only, while the campaign is open.
19//
20// It adds the envelope to the campaign's bond (API §3.1): its organizer only, by a
21// direct transaction, while the campaign is open, its bond unslashed and Bolo not
22// paused. The bond only grows, and the coins go on to the bond address: the
23// realm keeps nothing (INV-3).
24func PostBond(cur realm, id string) {
25 assertCurrent(cur)
26 if !cur.Previous().IsUserCall() {
27 panic(ErrNotUserCall)
28 }
29 runtime.AssertOriginCall()
30 c := campaignOf(id)
31 recordDeadline(cur, c)
32 if cur.Previous().Address() != c.organizer {
33 panic(ErrNotOrganizer)
34 }
35 amount := envelopeUgnot()
36 checkBond(c, amount)
37 c.state.bond += amount
38 banker.NewBanker(banker.BankerTypeRealmSend, cur).SendCoins(realmAddr(), bondAddr(c.id), chain.NewCoins(chain.NewCoin("ugnot", amount)))
39 chain.Emit(evBondPosted, "id", idString(c.id), "amount", itoa(amount), "bond", itoa(c.state.bond))
40}
41
42// checkBond refuses amount as more bond for c (API §3.1), in this order: nothing
43// sent, a campaign that isn't open, a slashed bond, a paused Bolo, and a bond that
44// would pass int64.
45func checkBond(c *campaign, amount int64) {
46 if amount <= 0 {
47 panic(ErrBadAmount)
48 }
49 assertOpen(c)
50 if c.state.bondSlashed {
51 panic(ErrBondSlashed)
52 }
53 if paused {
54 panic(ErrPaused)
55 }
56 if amount > math.MaxInt64-c.state.bond {
57 panic(ErrBadAmount)
58 }
59}
60
61// assertOpen refuses a campaign that isn't open, by its stored status:
62// ErrCampaignSettled once settled, ErrCampaignClosed otherwise. A passed deadline
63// is recorded before (API §3.1).
64func assertOpen(c *campaign) {
65 switch c.state.status {
66 case statusOpen:
67 case statusSettled:
68 panic(ErrCampaignSettled)
69 default:
70 panic(ErrCampaignClosed)
71 }
72}
73
74// PostUpdate posts an update on campaign id, shown under its story, and returns the
75// update's number: its organizer only. body may hold links, never an image.
76//
77// It appends an update to the campaign (API §3.1): its organizer only, by a direct
78// transaction, in every status and while paused. Updates are append-only, and each
79// resets the dormancy clock.
80func PostUpdate(cur realm, id, body string) string {
81 assertCurrent(cur)
82 if !cur.Previous().IsUserCall() {
83 panic(ErrNotUserCall)
84 }
85 runtime.AssertOriginCall()
86 c := campaignOf(id)
87 recordDeadline(cur, c)
88 if cur.Previous().Address() != c.organizer {
89 panic(ErrNotOrganizer)
90 }
91 body = checkStory(body, updateMin, updateMax, ErrUpdate)
92 s := c.state
93 seq := s.updateSeq.Next()
94 c.updates.Set(seq.Binary(), &update{seq: uint64(seq), at: now(), body: body})
95 s.updateCount++
96 s.lastUpdateAt = now()
97 chain.Emit(evUpdatePosted, "id", idString(c.id), "seq", idString(seq))
98 return idString(seq)
99}
100
101// HideImage hides campaign id's photo, for good: its organizer can, and so can Bolo's
102// moderator.
103//
104// Its organizer may, by a direct transaction, and so may the moderator, with the
105// master key — a DAO realm, once one holds the role, through C11's realm branch (API
106// §3.1, §3.5). The CID stays: it is a fingerprint, and never replaced. Hiding a
107// hidden photo, or a campaign with none, changes nothing.
108func HideImage(cur realm, id string) {
109 assertCurrent(cur)
110 if cur.Previous().IsUserCall() {
111 runtime.AssertOriginCall()
112 } else {
113 assertCodeCaller(cur)
114 }
115 c := campaignOf(id)
116 recordDeadline(cur, c)
117 by := hiderOrganizer
118 if cur.Previous().Address() != c.organizer { // an organizer is a user: CreateCampaign admits no other
119 assertSigner(cur, admin)
120 by = hiderModerator
121 }
122 if c.text.imageCID == "" || c.state.imageHidden {
123 return
124 }
125 c.state.imageHidden = true
126 chain.Emit(evImageHidden, "id", idString(c.id), "by", by)
127}