stakeseries.gno
3.69 Kb · 151 lines
1package kourtv3
2import (
3 "strconv"
4 "strings"
5 checkpoint "gno.land/p/g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr/checkpoint/v0"
6)
7const (
8 hourlyKeep = uint32(168)
9 trimBudget = 2
10 dailyBlocks = int64(17_280)
11 seriesRowCap = 400
12)
13func csKey(id uint64, suffix string) string {
14 return "cs:" + strconv.FormatUint(id, 10) + ":" + suffix
15}
16func ensureCSArch(c *Court) *checkpoint.Archive {
17 if c.csArch == nil {
18 c.csArch = checkpoint.NewArchive()
19 }
20 return c.csArch
21}
22func setIfChanged(a *checkpoint.Archive, s *checkpoint.Series, key string, e uint32, v int64) {
23 if v != s.Value() {
24 s.SetAt(a, key, e, v)
25 }
26}
27func recordPools(c *Court, cs *claimState) {
28 h := heightNow()
29 a := ensureCSArch(c)
30 hE := uint32(h/epochBlocks) + 1
31 dE := uint32(h/dailyBlocks) + 1
32 setIfChanged(a, &cs.yesH, csKey(cs.id, "yh"), hE, cs.yesStake)
33 setIfChanged(a, &cs.noH, csKey(cs.id, "nh"), hE, cs.noStake)
34 setIfChanged(a, &cs.yesD, csKey(cs.id, "yd"), dE, cs.yesStake)
35 setIfChanged(a, &cs.noD, csKey(cs.id, "nd"), dE, cs.noStake)
36 if hE > cs.histTrimmedTo {
37 cs.histTrimmedTo = hE
38 trimClaimHistory(c, cs, hE)
39 }
40}
41func trimClaimHistory(c *Court, cs *claimState, hE uint32) {
42 if c.csArch == nil || hE <= hourlyKeep+1 {
43 return
44 }
45 keep := hE - hourlyKeep
46 c.csArch.Trim(csKey(cs.id, "yh"), keep, trimBudget)
47 c.csArch.Trim(csKey(cs.id, "nh"), keep, trimBudget)
48}
49type histPoint struct {
50 e uint32
51 v int64
52}
53func collectSeriesKeyed(a *checkpoint.Archive, s *checkpoint.Series, key string) ([]histPoint, bool) {
54 pts := []histPoint{}
55 more := false
56 if a != nil && key != "" {
57 a.WalkDesc(key, func(e uint32, v int64) bool {
58 if len(pts) >= seriesRowCap {
59 more = true
60 return true
61 }
62 pts = append(pts, histPoint{e, v})
63 return false
64 })
65 }
66 for i, j := 0, len(pts)-1; i < j; i, j = i+1, j-1 {
67 pts[i], pts[j] = pts[j], pts[i]
68 }
69 if e1, pv := s.Prev(); e1 != 0 {
70 pts = append(pts, histPoint{e1, pv})
71 }
72 if e0 := s.Since(); e0 != 0 {
73 pts = append(pts, histPoint{e0, s.Value()})
74 }
75 return pts, more
76}
77func ClaimSeries(courtSlug string, claimID uint64, grain string) string {
78 c := mustCourt(courtSlug)
79 cs := mustClaim(c, claimID)
80 var width int64
81 var yS, nS *checkpoint.Series
82 var yKey, nKey string
83 switch grain {
84 case "hourly":
85 width = epochBlocks
86 yS, nS = &cs.yesH, &cs.noH
87 yKey, nKey = csKey(claimID, "yh"), csKey(claimID, "nh")
88 case "daily":
89 width = dailyBlocks
90 yS, nS = &cs.yesD, &cs.noD
91 yKey, nKey = csKey(claimID, "yd"), csKey(claimID, "nd")
92 default:
93 panic(`kourtv3: grain is "hourly" or "daily"`)
94 }
95 now := uint32(heightNow()/width) + 1
96 ys, moreY := collectSeriesKeyed(c.csArch, yS, yKey)
97 ns, moreN := collectSeriesKeyed(c.csArch, nS, nKey)
98 start := uint32(0)
99 if moreY && len(ys) > 0 && ys[0].e > start {
100 start = ys[0].e
101 }
102 if moreN && len(ns) > 0 && ns[0].e > start {
103 start = ns[0].e
104 }
105 more := moreY || moreN
106 curY, curN := int64(0), int64(0)
107 i, j := 0, 0
108 for i < len(ys) && ys[i].e < start {
109 curY = ys[i].v
110 i++
111 }
112 for j < len(ns) && ns[j].e < start {
113 curN = ns[j].v
114 j++
115 }
116 rows := []string{}
117 for i < len(ys) || j < len(ns) {
118 var e uint32
119 switch {
120 case i >= len(ys):
121 e = ns[j].e
122 case j >= len(ns):
123 e = ys[i].e
124 case ys[i].e < ns[j].e:
125 e = ys[i].e
126 default:
127 e = ns[j].e
128 }
129 if i < len(ys) && ys[i].e == e {
130 curY = ys[i].v
131 i++
132 }
133 if j < len(ns) && ns[j].e == e {
134 curN = ns[j].v
135 j++
136 }
137 rows = append(rows, strconv.FormatUint(uint64(e), 10)+":"+
138 strconv.FormatInt(curY, 10)+":"+strconv.FormatInt(curN, 10))
139 }
140 if len(rows) > seriesRowCap {
141 rows = rows[len(rows)-seriesRowCap:]
142 more = true
143 }
144 m := "0"
145 if more {
146 m = "1"
147 }
148 return grain + "," + strconv.FormatInt(width, 10) + "," +
149 strconv.FormatUint(uint64(now), 10) + "," + m + ";" +
150 strings.Join(rows, ",")
151}