Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}