lotto.gno
2.66 Kb · 90 lines
1package lotto
2
3import (
4 "chain"
5 "strconv"
6
7 "gno.land/p/akkadia/beta/accesscontrol"
8 "gno.land/p/nt/bptree/v0"
9 "gno.land/r/akkadia/beta/admin"
10)
11
12const AddNumberEvent = "AddNumber"
13
14var holdings = bptree.NewBPTree32()
15
16// AddNumber increments the holding once per successful call, including retries.
17// Only the direct admin or operator caller may register a number. Individual
18// ticket identities, issuance times and past round balances are not retained.
19func AddNumber(cur realm, owner address, number uint64) {
20 accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
21 assertValidAddress(owner)
22 key := strconv.FormatUint(number, 10) + ":" + owner.String()
23 count := uint64(0)
24 if current := holdings.Get(key); current != nil {
25 count = current.(uint64)
26 }
27 if count == ^uint64(0) {
28 panic("number count overflow")
29 }
30 count++
31 holdings.Set(key, count)
32 chain.Emit(
33 AddNumberEvent,
34 "owner", owner.String(),
35 "number", strconv.FormatUint(number, 10),
36 "count", strconv.FormatUint(count, 10),
37 )
38}
39
40func DeleteAll(cur realm) {
41 accesscontrol.AssertIsAdmin(0, cur, admin.IsAdmin)
42 holdings = bptree.NewBPTree32()
43}
44
45// GetNumberCount returns zero when the valid owner has no holding for number.
46func GetNumberCount(number uint64, owner address) uint64 {
47 assertValidAddress(owner)
48 current := holdings.Get(strconv.FormatUint(number, 10) + ":" + owner.String())
49 if current == nil {
50 return 0
51 }
52 return current.(uint64)
53}
54
55// ListOwnersByNumber returns fresh values in ascending address order. A cursor
56// need not be stored. Each row carries the page cursor; an empty cursor or empty
57// list ends pagination. Counts are decimal strings to preserve uint64 precision.
58// Pages reflect current state, not a historical snapshot.
59func ListOwnersByNumber(number uint64, afterOwner string, limit int) []map[string]string {
60 if afterOwner != "" {
61 assertValidAddress(address(afterOwner))
62 }
63 assertListLimit(limit)
64 numberStr := strconv.FormatUint(number, 10)
65 prefix := numberStr + ":"
66 start := prefix
67 if afterOwner != "" {
68 // Valid address strings cannot contain NUL, so this excludes the cursor
69 // without visiting it or scanning preceding owners.
70 start += afterOwner + "\x00"
71 }
72 result := make([]map[string]string, 0, limit)
73 nextOwner := ""
74 holdings.Iterate(start, numberStr+";", func(key string, count any) bool {
75 if len(result) == limit {
76 nextOwner = result[len(result)-1]["owner"]
77 return true
78 }
79 result = append(result, map[string]string{
80 "owner": key[len(prefix):],
81 "count": strconv.FormatUint(count.(uint64), 10),
82 "nextOwner": "",
83 })
84 return false
85 })
86 for _, item := range result {
87 item["nextOwner"] = nextOwner
88 }
89 return result
90}