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.

moderation.gno

4.36 Kb · 141 lines
  1package bolo
  2
  3import (
  4	"chain"
  5	"chain/banker"
  6	"chain/runtime"
  7	"strconv"
  8)
  9
 10// Moderation (API §3.5): visibility, and the organizer's own bond — nothing else.
 11// Every write here starts with C11's realm branch, then assertSigner: the master key,
 12// then the admin's address, before anything is read. Then the campaign and its passed
 13// deadline, since a slash's window runs from closedAt. The rules live in plain
 14// helpers, which unit tests drive directly; the writes keep the caller checks, the
 15// send and the events. Withdraw, Refund and Finalize never read what is written here
 16// (INV-4).
 17
 18// Delist takes the campaign off the active lists, with a public notice, and with slash
 19// moves its bond to the slashed address, where nothing ever spends it (INV-8).
 20// Delisting again replaces the notice, and may slash a bond not yet slashed.
 21func Delist(cur realm, id, notice string, slash bool) {
 22	assertCurrent(cur)
 23	if cur.Previous().IsUserCall() {
 24		runtime.AssertOriginCall()
 25	} else {
 26		assertCodeCaller(cur)
 27	}
 28	assertSigner(cur, admin)
 29	c := campaignOf(id)
 30	recordDeadline(cur, c)
 31	notice = checkText(notice, noticeMin, noticeMax, false, ErrNotice)
 32	if slash {
 33		bond := bookSlash(c)
 34		banker.NewBanker(banker.BankerTypeRealmSend, bondSub(cur, c)).SendCoins(bondAddr(c.id), slashedAddr(), chain.NewCoins(chain.NewCoin("ugnot", bond)))
 35	}
 36	s := c.state
 37	s.listed, s.notice = false, notice
 38	dropActive(c)
 39	chain.Emit(evDelisted, "id", idString(c.id), "notice", notice, "slashed", strconv.FormatBool(slash))
 40}
 41
 42// bookSlash checks a slash of c's bond and enters it in the books, before the coins
 43// move (API §3.5), and returns what moves: the bond, never the deposit. It refuses, in
 44// order, no bond posted, a bond already slashed, a hold that has ended — once closed,
 45// BondHold after closedAt, settled or not — and a bond address holding less than the
 46// bond (INV-7).
 47func bookSlash(c *campaign) int64 {
 48	s := c.state
 49	if s.bond == 0 {
 50		panic(ErrNoBond)
 51	}
 52	if s.bondSlashed {
 53		panic(ErrBondSlashed)
 54	}
 55	if s.status != statusOpen && now() >= s.closedAt+c.params.BondHold {
 56		panic(ErrHoldEnded)
 57	}
 58	if banker.NewReadonlyBanker().GetCoin(bondAddr(c.id), "ugnot") < s.bond {
 59		panic(ErrBooksMismatch)
 60	}
 61	s.bondSlashed = true
 62	return s.bond
 63}
 64
 65// Relist puts a delisted campaign back on the lists, and into the active indexes if
 66// it is still open (API §3.5). Its notice stays, as does a slash. Relisting a listed
 67// campaign changes nothing.
 68func Relist(cur realm, id string) {
 69	assertCurrent(cur)
 70	if cur.Previous().IsUserCall() {
 71		runtime.AssertOriginCall()
 72	} else {
 73		assertCodeCaller(cur)
 74	}
 75	assertSigner(cur, admin)
 76	c := campaignOf(id)
 77	recordDeadline(cur, c)
 78	s := c.state
 79	if s.listed {
 80		return
 81	}
 82	s.listed = true
 83	if s.status == statusOpen {
 84		addActive(c)
 85	}
 86	chain.Emit(evRelisted, "id", idString(c.id))
 87}
 88
 89// SetNotice attaches a public warning to the campaign without delisting it, or clears
 90// it with "" (API §3.5). An unchanged notice changes nothing.
 91func SetNotice(cur realm, id, notice string) {
 92	assertCurrent(cur)
 93	if cur.Previous().IsUserCall() {
 94		runtime.AssertOriginCall()
 95	} else {
 96		assertCodeCaller(cur)
 97	}
 98	assertSigner(cur, admin)
 99	c := campaignOf(id)
100	recordDeadline(cur, c)
101	notice = checkNotice(c, notice)
102	if notice == c.state.notice {
103		return
104	}
105	c.state.notice = notice
106	chain.Emit(evNoticeSet, "id", idString(c.id), "notice", notice)
107}
108
109// checkNotice returns notice as SetNotice stores it (API §3.5): "" clears, except on a
110// delisted campaign, which keeps a public reason; any other text is normalized, 1–280
111// characters. It panics with ErrNotice otherwise.
112func checkNotice(c *campaign, notice string) string {
113	if notice != "" {
114		return checkText(notice, noticeMin, noticeMax, false, ErrNotice)
115	}
116	if !c.state.listed {
117		panic(ErrNotice)
118	}
119	return ""
120}
121
122// HideMessage hides one gift's message (API §3.5): visibility only. The receipt, its
123// amount and its donor stay, and so does the stored text. Hiding a hidden message, or
124// a receipt without one, changes nothing.
125func HideMessage(cur realm, id string, seq int64) {
126	assertCurrent(cur)
127	if cur.Previous().IsUserCall() {
128		runtime.AssertOriginCall()
129	} else {
130		assertCodeCaller(cur)
131	}
132	assertSigner(cur, admin)
133	c := campaignOf(id)
134	recordDeadline(cur, c)
135	r := receiptOf(c, seq)
136	if r.message == "" || r.messageHidden {
137		return
138	}
139	r.messageHidden = true
140	chain.Emit(evMessageHidden, "id", idString(c.id), "seq", itoa(seq))
141}