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

supersede.gno

3.24 Kb · 133 lines
  1package kourtv3
  2import (
  3	"strconv"
  4	"strings"
  5	bptree "gno.land/p/nt/bptree/v0"
  6)
  7const (
  8	maxSupIn = 8
  9	maxSupInPerAuthor = 2
 10)
 11type supEdge struct {
 12	to     uint64
 13	author address
 14	at     int64
 15}
 16func ensureSup(c *Court) {
 17	if c.supOf == nil {
 18		c.supOf = bptree.NewBPTree32()
 19	}
 20	if c.supBy == nil {
 21		c.supBy = bptree.NewBPTree32()
 22	}
 23}
 24func supersedeOrdered(from, to *claimState) bool {
 25	if from.openedAtTime != 0 && to.openedAtTime != 0 {
 26		return from.openedAtTime >= to.openedAtTime+deadClaimSecs
 27	}
 28	return from.openedAt >= to.openedAt+deadClaimTimeout
 29}
 30func SupersedeClaim(cur realm, courtSlug string, from, to uint64) {
 31	if !cur.IsCurrent() {
 32		panic(errStaleRealm)
 33	}
 34	who := cur.Previous().Address()
 35	c := mustCourt(courtSlug)
 36	if from == to {
 37		panic("kourtv3: a claim cannot supersede itself")
 38	}
 39	f := mustClaim(c, from)
 40	t := mustClaim(c, to)
 41	if f.author != who {
 42		cm := ensureMod(c)
 43		if !cm.members.Has(who.String()) {
 44			panic("kourtv3: only the re-filing claim's author or a court moderator may record it")
 45		}
 46		requireActiveMod(cm, who)
 47	}
 48	if !t.closed {
 49		panic("kourtv3: only a claim that died unanswered can be superseded")
 50	}
 51	if !supersedeOrdered(f, t) {
 52		panic("kourtv3: a re-filing must come after the claim it re-files could have died")
 53	}
 54	ensureSup(c)
 55	if c.supOf.Get(beClaimKey(from)) != nil {
 56		panic("kourtv3: that claim already re-files one; remove that edge first")
 57	}
 58	inN, mine := 0, 0
 59	p := beClaimKey(to)
 60	c.supBy.Iterate(p, "", func(key string, v any) bool {
 61		if !strings.HasPrefix(key, p) {
 62			return true
 63		}
 64		inN++
 65		if e, ok := v.(*supEdge); ok && e.author == who {
 66			mine++
 67		}
 68		return false
 69	})
 70	if inN >= maxSupIn {
 71		panic("kourtv3: that claim has already been re-filed " + strconv.Itoa(maxSupIn) + " times")
 72	}
 73	if mine >= maxSupInPerAuthor {
 74		panic("kourtv3: you already record " + strconv.Itoa(maxSupInPerAuthor) +
 75			" re-filings of that claim")
 76	}
 77	e := &supEdge{to: to, author: who, at: heightNow()}
 78	c.supOf.Set(beClaimKey(from), e)
 79	c.supBy.Set(beClaimKey(to)+beClaimKey(from), e)
 80}
 81func RemoveSupersede(cur realm, courtSlug string, from uint64) {
 82	if !cur.IsCurrent() {
 83		panic(errStaleRealm)
 84	}
 85	who := cur.Previous().Address()
 86	c := mustCourt(courtSlug)
 87	if c.supOf == nil {
 88		panic("kourtv3: that claim re-files nothing")
 89	}
 90	v := c.supOf.Get(beClaimKey(from))
 91	if v == nil {
 92		panic("kourtv3: that claim re-files nothing")
 93	}
 94	e := v.(*supEdge)
 95	requireEdgeRemover(c, e.author, who)
 96	c.supOf.Remove(beClaimKey(from))
 97	c.supBy.Remove(beClaimKey(e.to) + beClaimKey(from))
 98}
 99func ClaimSupersedes(courtSlug string, claimID uint64) string {
100	c := mustCourt(courtSlug)
101	mustClaim(c, claimID)
102	var b strings.Builder
103	b.WriteString("of:")
104	if c.supOf != nil {
105		if v := c.supOf.Get(beClaimKey(claimID)); v != nil {
106			e := v.(*supEdge)
107			if !assocTextGone(c, e.to) {
108				b.WriteString(strconv.FormatUint(e.to, 10))
109			}
110		}
111	}
112	b.WriteString(";by:")
113	if c.supBy != nil {
114		p := beClaimKey(claimID)
115		n := 0
116		c.supBy.Iterate(p, "", func(key string, v any) bool {
117			if !strings.HasPrefix(key, p) {
118				return true
119			}
120			other := claimIDFromKey(key[8:])
121			if assocTextGone(c, other) {
122				return false
123			}
124			if n > 0 {
125				b.WriteString(",")
126			}
127			b.WriteString(strconv.FormatUint(other, 10))
128			n++
129			return false
130		})
131	}
132	return b.String()
133}