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

meta.gno

9.87 Kb · 380 lines
  1package kourt
  2import (
  3	"chain"
  4	"strconv"
  5	"strings"
  6	unsafe "chain/runtime/unsafe"
  7	"math/overflow"
  8	bptree "gno.land/p/nt/bptree/v0"
  9)
 10const metaRealmPath = "gno.land/r/g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6/kourt"
 11const (
 12	metaPrefix   = "mod:"
 13	metaCourtNm  = "Meta"
 14	metaExecuted = "MetaExecuted"
 15)
 16const (
 17	verbUnhide    = "unhide"
 18	verbClear     = "clear"
 19	verbHide      = "hide"
 20	verbSuspend   = "suspend"
 21	verbUnsuspend = "unsuspend"
 22	verbSetmods   = "setmods"
 23	verbNewset = "newset"
 24)
 25func verbIsAggressive(v string) bool {
 26	return v == verbHide || v == verbSuspend || v == verbUnsuspend || v == verbSetmods
 27}
 28type modParse struct {
 29	valid     bool
 30	verb      string
 31	court     string
 32	target    uint64
 33	hasTarget bool
 34	poisoned  bool
 35}
 36type metaState struct {
 37	parses   *bptree.BPTree
 38	bindings *bptree.BPTree
 39	latches  *bptree.BPTree
 40	executed *bptree.BPTree
 41}
 42var meta = &metaState{
 43	parses:   bptree.NewBPTree32(),
 44	bindings: bptree.NewBPTree32(),
 45	latches:  bptree.NewBPTree32(),
 46	executed: bptree.NewBPTree32(),
 47}
 48func init() {
 49	startCourt(unsafe.OriginCaller(), chain.PackageAddress(metaRealmPath), metaSlug, metaCourtNm, 0)
 50}
 51func parseModTitle(title string) modParse {
 52	p := modParse{}
 53	if !strings.HasPrefix(title, metaPrefix) {
 54		return p
 55	}
 56	rest := title[len(metaPrefix):]
 57	slash := strings.Index(rest, ":")
 58	if slash < 0 {
 59		return p
 60	}
 61	verb := rest[:slash]
 62	arg := rest[slash+1:]
 63	switch verb {
 64	case verbUnhide, verbClear, verbHide, verbSetmods, verbNewset:
 65		cut := strings.Index(arg, "/")
 66		if cut <= 0 || cut == len(arg)-1 {
 67			return p
 68		}
 69		id, err := strconv.ParseUint(arg[cut+1:], 10, 64)
 70		if err != nil || id == 0 {
 71			return p
 72		}
 73		p.court, p.target, p.hasTarget = arg[:cut], id, true
 74	case verbSuspend, verbUnsuspend:
 75		if arg == "" || strings.Contains(arg, "/") {
 76			return p
 77		}
 78		p.court = arg
 79	default:
 80		return p
 81	}
 82	for i := 0; i < len(p.court); i++ {
 83		ch := p.court[i]
 84		if !((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-') {
 85			return modParse{}
 86		}
 87	}
 88	p.verb, p.valid = verb, true
 89	return p
 90}
 91func latchKey(p modParse) string {
 92	switch p.verb {
 93	case verbSetmods:
 94		return "set:" + p.court
 95	case verbNewset:
 96		return "fold:" + p.court
 97	case verbSuspend, verbUnsuspend:
 98		return "sus:" + p.court
 99	default:
100		return "item:" + p.court + ":" + strconv.FormatUint(p.target, 10)
101	}
102}
103func onClaimOpened(c *Court, cs *claimState) {
104	if c.id != metaSlug {
105		return
106	}
107	p := parseModTitle(cs.title)
108	meta.parses.Set(beClaimKey(cs.id), &p)
109	cs.noAccuracy = p.valid && p.verb == verbNewset
110	if p.valid {
111		pendingEnter(c, cs)
112	}
113}
114func onTitleEdited(c *Court, cs *claimState) {
115	if c.id != metaSlug {
116		return
117	}
118	p := parseModTitle(cs.title)
119	if prev := meta.parses.Get(beClaimKey(cs.id)); prev != nil && prev.(*modParse).poisoned {
120		p.poisoned = true
121	}
122	meta.parses.Set(beClaimKey(cs.id), &p)
123	cs.noAccuracy = p.valid && p.verb == verbNewset
124	if p.valid || cs.seeded {
125		pendingEnter(c, cs)
126	} else {
127		pendingExit(c, cs)
128	}
129}
130func onClaimAnswered(c *Court, cs *claimState) {
131	if c.id != metaSlug {
132		return
133	}
134	v := meta.parses.Get(beClaimKey(cs.id))
135	if v == nil {
136		return
137	}
138	p := v.(*modParse)
139	if !p.valid || p.poisoned {
140		return
141	}
142	k := latchKey(*p)
143	if held := meta.latches.Get(k); held != nil {
144		if held.(uint64) != cs.id {
145			panic("kourtv2: another appeal already holds this target's review slot")
146		}
147	}
148	meta.latches.Set(k, cs.id)
149	bind := *p
150	meta.bindings.Set(beClaimKey(cs.id), &bind)
151}
152func releaseMetaLatchIfMeta(c *Court, cs *claimState) {
153	if c.id != metaSlug {
154		return
155	}
156	releaseMetaLatch(cs)
157}
158func releaseMetaLatch(cs *claimState) {
159	v := meta.bindings.Get(beClaimKey(cs.id))
160	if v == nil {
161		return
162	}
163	k := latchKey(*v.(*modParse))
164	if held := meta.latches.Get(k); held != nil && held.(uint64) == cs.id {
165		meta.latches.Remove(k)
166	}
167}
168func poisonMetaParse(claimID uint64) {
169	if v := meta.parses.Get(beClaimKey(claimID)); v != nil {
170		v.(*modParse).poisoned = true
171	}
172}
173func metaVerdictExpired(cs *claimState, votingBlocks int64) bool {
174	passed, known := pastDeadline(cs.verdictAtTime, blocksToSecs(votingBlocks)+1)
175	return (known && passed) || (!known && heightNow() > cs.verdictAt+votingBlocks)
176}
177func ExecuteMetaVerdict(cur realm, metaClaimID uint64) {
178	if !cur.IsCurrent() {
179		panic(errStaleRealm)
180	}
181	mc := mustCourt(metaSlug)
182	cs := mustClaim(mc, metaClaimID)
183	bv := meta.bindings.Get(beClaimKey(metaClaimID))
184	if bv == nil {
185		panic("kourtv2: this claim carries no binding appeal")
186	}
187	p := *bv.(*modParse)
188	if !Exists(p.court) {
189		panic("kourtv2: the target court does not exist")
190	}
191	tc := mustCourt(p.court)
192	if tc.createdAt >= cs.openedAt {
193		panic("kourtv2: the target court does not predate this appeal")
194	}
195	var target *claimState
196	if p.hasTarget && p.verb != verbSetmods && p.verb != verbNewset {
197		target = mustClaim(tc, p.target)
198		if target.openedAt >= cs.openedAt {
199			panic("kourtv2: the target claim does not predate this appeal")
200		}
201	}
202	if p.verb == verbSetmods {
203		if cand := ensureMod(tc).mustCandidate(p.target); cand.at >= cs.openedAt {
204			panic("kourtv2: the candidate set does not predate this appeal")
205		}
206	}
207	if p.verb == verbNewset {
208		if prop := ensureMod(tc).mustFolderProposal(p.target); prop.at >= cs.openedAt {
209			panic("kourtv2: the folder proposal does not predate this appeal")
210		}
211	}
212	if cs.verdictAt == 0 || cs.provClose {
213		panic("kourtv2: no final verdict")
214	}
215	if int(cs.provisional) != sideYES {
216		panic("kourtv2: the verdict was not YES")
217	}
218	if verbIsAggressive(p.verb) {
219		if cs.route != "vote" || cs.decidedRounds == 0 {
220			panic("kourtv2: this verb needs a decided contested vote, not silence")
221		}
222		if p.verb != verbHide && !cs.credEligible {
223			panic("kourtv2: a manufactured contest cannot seize authority — this verb needs weight voted against it")
224		}
225	}
226	if meta.executed.Has(beClaimKey(metaClaimID)) {
227		panic("kourtv2: already executed")
228	}
229	if metaVerdictExpired(cs, mc.params.votingBlocks) {
230		panic("kourtv2: this verdict has expired; re-appeal")
231	}
232	cm := ensureMod(tc)
233	switch p.verb {
234	case verbSetmods:
235		if cm.setActHeight > cs.verdictAt {
236			panic("kourtv2: the moderator set changed after this verdict; re-appeal")
237		}
238		if cm.suspended && cm.suspendActByGlobal > cs.openedAt {
239			panic("kourtv2: the global DAO changed this court's suspension under this appeal; re-appeal")
240		}
241	case verbSuspend, verbUnsuspend:
242		if cm.lastElectionAt > cs.openedAt || cm.setActHeight > cs.verdictAt {
243			panic("kourtv2: the moderator set changed under this appeal; re-appeal")
244		}
245		if cm.suspendActByGlobal > cs.openedAt &&
246			cm.suspended == (p.verb == verbUnsuspend) {
247			panic("kourtv2: the global DAO changed this court's suspension under this appeal; re-appeal")
248		}
249	case verbUnhide:
250		if cm.lastElectionAt > cs.openedAt {
251			panic("kourtv2: the moderator set changed under this appeal; re-appeal")
252		}
253	}
254	meta.executed.Set(beClaimKey(metaClaimID), true)
255	applyMetaVerb(mc, tc, cm, p, cs, target)
256	releaseMetaLatch(cs)
257	chain.Emit(metaExecuted,
258		"appeal", strconv.FormatUint(metaClaimID, 10),
259		"verb", p.verb, "court", p.court,
260		"target", strconv.FormatUint(p.target, 10),
261	)
262}
263func applyMetaVerb(mc, tc *Court, cm *courtMod, p modParse, cs, target *claimState) {
264	by := cs.answerer
265	switch p.verb {
266	case verbUnhide:
267		clm := lookupClaimMod(cm, p.target)
268		if clm == nil || !clm.court {
269			return
270		}
271		clearCourtBitByMeta(tc, p.target, by)
272	case verbClear:
273		clearMetaBit(tc, p.target, by)
274	case verbHide:
275		setMetaBit(tc, p.target, by)
276	case verbSuspend:
277		suspendSet(tc, by)
278	case verbUnsuspend:
279		unsuspendSet(tc, by)
280	case verbNewset:
281		prop := cm.mustFolderProposal(p.target)
282		if folderNameTaken(cm, prop.name) {
283			panic("kourtv2: a folder by that name already exists")
284		}
285		if prop.parent != 0 {
286			if pf := cm.mustFolder(prop.parent); pf.retired || pf.purged {
287				panic("kourtv2: the parent heading is no longer in the tree")
288			}
289		}
290		fid := newFolderCore(cm, prop.name, prop.desc, prop.parent)
291		emitModAct(tc.id, 0, "folder-create", by)
292		chain.Emit("FolderCreated",
293			"court", tc.id, "by", "meta",
294			"folder", strconv.FormatUint(fid, 10),
295			"proposal", strconv.FormatUint(p.target, 10),
296			"height", eventHeight(),
297		)
298	case verbSetmods:
299		cand := cm.mustCandidate(p.target)
300		installModSet(tc, cm, cand, heightNow(), false)
301		chain.Emit("ModSetInstalled",
302			"court", tc.id, "by", "meta",
303			"candidate", strconv.FormatUint(cand.id, 10),
304			"height", eventHeight(),
305		)
306	}
307}
308func AppealParse(claimID uint64) (verb, court string, target uint64, valid bool) {
309	v := meta.parses.Get(beClaimKey(claimID))
310	if v == nil {
311		return "", "", 0, false
312	}
313	p := v.(*modParse)
314	return p.verb, p.court, p.target, p.valid
315}
316func AppealExecuted(claimID uint64) bool { return meta.executed.Has(beClaimKey(claimID)) }
317func AppealLatchHolder(verb, court string, target uint64) uint64 {
318	p := modParse{verb: verb, court: court, target: target}
319	if v := meta.latches.Get(latchKey(p)); v != nil {
320		return v.(uint64)
321	}
322	return 0
323}
324var franchise = bptree.NewBPTree32()
325func accrueFranchise(c *Court, who address, spent int64) {
326	if c.id == metaSlug || spent <= 0 {
327		return
328	}
329	k := who.String()
330	prev := int64(0)
331	if v := franchise.Get(k); v != nil {
332		prev = v.(int64)
333	}
334	sum, ok := overflow.Add64(prev, spent)
335	if !ok {
336		return
337	}
338	franchise.Set(k, sum)
339}
340func ClaimMetaFranchise(cur realm) int64 {
341	if !cur.IsCurrent() {
342		panic(errStaleRealm)
343	}
344	who := cur.Previous().Address()
345	k := who.String()
346	v := franchise.Get(k)
347	if v == nil {
348		return 0
349	}
350	owed := v.(int64)
351	if owed <= 0 {
352		return 0
353	}
354	mc := mustCourt(metaSlug)
355	touch(mc)
356	delta, used := mc.crv.Minted(mc.minted, owed)
357	if delta <= 0 {
358		return 0
359	}
360	mc.minted += delta
361	recordPrice(mc)
362	mc.coin.Mint(who, delta)
363	if used >= owed {
364		franchise.Remove(k)
365	} else {
366		franchise.Set(k, owed-used)
367	}
368	chain.Emit("FranchiseClaimed",
369		"who", who.String(),
370		"burned", strconv.FormatInt(used, 10),
371		"minted", strconv.FormatInt(delta, 10),
372	)
373	return delta
374}
375func FranchiseOf(who address) int64 {
376	if v := franchise.Get(who.String()); v != nil {
377		return v.(int64)
378	}
379	return 0
380}