util.gno
5.10 Kb · 200 lines
1package v1
2
3import (
4 "math"
5 "strconv"
6
7 u256 "gno.land/p/gnoswap/uint256"
8 bptree "gno.land/p/nt/bptree/v0"
9 ufmt "gno.land/p/nt/ufmt/v0"
10 "gno.land/p/onbloc/json"
11)
12
13// marshal data to json string
14func marshal(data *json.Node) string {
15 b, err := json.Marshal(data)
16 if err != nil {
17 panic(err.Error())
18 }
19
20 return string(b)
21}
22
23// formatUint formats a uint64 to a string
24func formatUint(v uint64) string {
25 return strconv.FormatUint(v, 10)
26}
27
28// formatInt formats an int64 to a string
29func formatInt(v int64) string {
30 return strconv.FormatInt(v, 10)
31}
32
33// getUint64FromTree returns the uint64 value from the tree
34func getUint64FromTree(tree *bptree.BPTree, key string) uint64 {
35 value, exists := tree.Get(key)
36 if !exists {
37 return 0
38 }
39
40 v, ok := value.(uint64)
41 if !ok {
42 panic(ufmt.Sprintf("failed to cast value to uint64: %T", value))
43 }
44
45 return v
46}
47
48// updateUint64InTree updates the uint64 value in the tree
49func updateUint64InTree(tree *bptree.BPTree, key string, delta uint64, add bool) uint64 {
50 current := getUint64FromTree(tree, key)
51 var newValue uint64
52 if add {
53 newValue = safeAddUint64(current, delta)
54 } else {
55 if current < delta {
56 panic(makeErrorWithDetails(
57 errNotEnoughBalance,
58 ufmt.Sprintf("not enough balance: current(%d) < requested(%d)", current, delta),
59 ))
60 }
61 newValue = safeSubUint64(current, delta)
62 }
63
64 tree.Set(key, newValue)
65
66 return newValue
67}
68
69// milliToSec converts milliseconds to seconds
70func milliToSec(ms int64) int64 {
71 var msPerSec int64 = 1000
72 return ms / msPerSec
73}
74
75// safeConvertToInt64 safely converts a *u256.Uint value to an int64, ensuring no overflow.
76//
77// This function attempts to convert the given *u256.Uint value to an int64. If the value exceeds
78// the maximum allowable range for int64 (`2^63 - 1`), it triggers a panic with a descriptive error message.
79//
80// Parameters:
81// - value (*u256.Uint): The unsigned 256-bit integer to be converted.
82//
83// Returns:
84// - int64: The converted value if it falls within the int64 range.
85//
86// Panics:
87// - If the `value` exceeds the range of int64, the function will panic with an error indicating
88// the overflow and the original value.
89func safeConvertToInt64(value *u256.Uint) int64 {
90 const INT64_MAX = 9223372036854775807
91 const MAX_INT64 = "9223372036854775807"
92
93 res, overflow := value.Uint64WithOverflow()
94 if overflow || res > uint64(INT64_MAX) {
95 panic(ufmt.Sprintf(
96 "amount(%s) overflows int64 range (max %s)",
97 value.ToString(),
98 MAX_INT64,
99 ))
100 }
101 return int64(res)
102}
103
104// safeAddInt64 performs safe addition of int64 values, panicking on overflow or underflow
105func safeAddInt64(a, b int64) int64 {
106 if a > 0 && b > math.MaxInt64-a {
107 panic("int64 addition overflow")
108 }
109
110 if a < 0 && b < math.MinInt64-a {
111 panic("int64 addition underflow")
112 }
113
114 return a + b
115}
116
117// safeSubInt64 performs safe subtraction of int64 values, panicking on overflow or underflow
118func safeSubInt64(a, b int64) int64 {
119 if b > 0 && a < math.MinInt64+b {
120 panic("int64 subtraction underflow")
121 }
122
123 if b < 0 && a > math.MaxInt64+b {
124 panic("int64 subtraction overflow")
125 }
126
127 return a - b
128}
129
130// safeAddUint64 performs safe addition of uint64 values, panicking on overflow
131func safeAddUint64(a, b uint64) uint64 {
132 if a > math.MaxUint64-b {
133 panic("uint64 addition overflow")
134 }
135
136 return a + b
137}
138
139// safeSubUint64 performs safe subtraction of uint64 values, panicking on underflow
140func safeSubUint64(a, b uint64) uint64 {
141 if a < b {
142 panic("uint64 subtraction underflow")
143 }
144
145 return a - b
146}
147
148// userHistoryKeySeparator is chosen to be lexicographically smaller than any
149const (
150 userHistoryKeySeparator = "|"
151 userHistoryKeySeparatorNextWord = string(int32('|') + 1)
152)
153
154// userHistoryTimestampWidth is the zero-padded width that fits the maximum
155const userHistoryTimestampWidth = 20
156
157// makeUserHistoryKey builds the composite key "addr|paddedTimestamp" used in the user delegation history BPTree.
158func makeUserHistoryKey(addrStr string, timestamp int64) string {
159 return addrStr + userHistoryKeySeparator + padTimestamp(timestamp)
160}
161
162// userHistoryKeyRange returns the half-open prefix range that covers all composite keys belonging to addrStr.
163func userHistoryKeyRange(addrStr string) (lo, hi string) {
164 lo = addrStr + userHistoryKeySeparator
165 hi = addrStr + userHistoryKeySeparatorNextWord
166
167 return lo, hi
168}
169
170// padTimestamp left-pads timestamp with zeros to userHistoryTimestampWidth so
171// that lexicographic ordering of the resulting strings matches numeric order.
172func padTimestamp(timestamp int64) string {
173 s := strconv.FormatInt(timestamp, 10)
174 if len(s) >= userHistoryTimestampWidth {
175 return s
176 }
177
178 pad := make([]byte, userHistoryTimestampWidth-len(s))
179 for i := range pad {
180 pad[i] = '0'
181 }
182
183 return string(pad) + s
184}
185
186// parseUserHistoryKey splits a composite key produced by makeUserHistoryKey back into its address and timestamp components.
187func parseUserHistoryKey(key string) (addrStr string, timestamp int64, ok bool) {
188 for i := len(key) - 1; i >= 0; i-- {
189 if key[i] == userHistoryKeySeparator[0] {
190 ts, err := strconv.ParseInt(key[i+1:], 10, 64)
191 if err != nil {
192 return "", 0, false
193 }
194
195 return key[:i], ts, true
196 }
197 }
198
199 return "", 0, false
200}