package v1 import ( "math" "strconv" u256 "gno.land/p/gnoswap/uint256" bptree "gno.land/p/nt/bptree/v0" ufmt "gno.land/p/nt/ufmt/v0" "gno.land/p/onbloc/json" ) // marshal data to json string func marshal(data *json.Node) string { b, err := json.Marshal(data) if err != nil { panic(err.Error()) } return string(b) } // formatUint formats a uint64 to a string func formatUint(v uint64) string { return strconv.FormatUint(v, 10) } // formatInt formats an int64 to a string func formatInt(v int64) string { return strconv.FormatInt(v, 10) } // getUint64FromTree returns the uint64 value from the tree func getUint64FromTree(tree *bptree.BPTree, key string) uint64 { value, exists := tree.Get(key) if !exists { return 0 } v, ok := value.(uint64) if !ok { panic(ufmt.Sprintf("failed to cast value to uint64: %T", value)) } return v } // updateUint64InTree updates the uint64 value in the tree func updateUint64InTree(tree *bptree.BPTree, key string, delta uint64, add bool) uint64 { current := getUint64FromTree(tree, key) var newValue uint64 if add { newValue = safeAddUint64(current, delta) } else { if current < delta { panic(makeErrorWithDetails( errNotEnoughBalance, ufmt.Sprintf("not enough balance: current(%d) < requested(%d)", current, delta), )) } newValue = safeSubUint64(current, delta) } tree.Set(key, newValue) return newValue } // milliToSec converts milliseconds to seconds func milliToSec(ms int64) int64 { var msPerSec int64 = 1000 return ms / msPerSec } // safeConvertToInt64 safely converts a *u256.Uint value to an int64, ensuring no overflow. // // This function attempts to convert the given *u256.Uint value to an int64. If the value exceeds // the maximum allowable range for int64 (`2^63 - 1`), it triggers a panic with a descriptive error message. // // Parameters: // - value (*u256.Uint): The unsigned 256-bit integer to be converted. // // Returns: // - int64: The converted value if it falls within the int64 range. // // Panics: // - If the `value` exceeds the range of int64, the function will panic with an error indicating // the overflow and the original value. func safeConvertToInt64(value *u256.Uint) int64 { const INT64_MAX = 9223372036854775807 const MAX_INT64 = "9223372036854775807" res, overflow := value.Uint64WithOverflow() if overflow || res > uint64(INT64_MAX) { panic(ufmt.Sprintf( "amount(%s) overflows int64 range (max %s)", value.ToString(), MAX_INT64, )) } return int64(res) } // safeAddInt64 performs safe addition of int64 values, panicking on overflow or underflow func safeAddInt64(a, b int64) int64 { if a > 0 && b > math.MaxInt64-a { panic("int64 addition overflow") } if a < 0 && b < math.MinInt64-a { panic("int64 addition underflow") } return a + b } // safeSubInt64 performs safe subtraction of int64 values, panicking on overflow or underflow func safeSubInt64(a, b int64) int64 { if b > 0 && a < math.MinInt64+b { panic("int64 subtraction underflow") } if b < 0 && a > math.MaxInt64+b { panic("int64 subtraction overflow") } return a - b } // safeAddUint64 performs safe addition of uint64 values, panicking on overflow func safeAddUint64(a, b uint64) uint64 { if a > math.MaxUint64-b { panic("uint64 addition overflow") } return a + b } // safeSubUint64 performs safe subtraction of uint64 values, panicking on underflow func safeSubUint64(a, b uint64) uint64 { if a < b { panic("uint64 subtraction underflow") } return a - b } // userHistoryKeySeparator is chosen to be lexicographically smaller than any const ( userHistoryKeySeparator = "|" userHistoryKeySeparatorNextWord = string(int32('|') + 1) ) // userHistoryTimestampWidth is the zero-padded width that fits the maximum const userHistoryTimestampWidth = 20 // makeUserHistoryKey builds the composite key "addr|paddedTimestamp" used in the user delegation history BPTree. func makeUserHistoryKey(addrStr string, timestamp int64) string { return addrStr + userHistoryKeySeparator + padTimestamp(timestamp) } // userHistoryKeyRange returns the half-open prefix range that covers all composite keys belonging to addrStr. func userHistoryKeyRange(addrStr string) (lo, hi string) { lo = addrStr + userHistoryKeySeparator hi = addrStr + userHistoryKeySeparatorNextWord return lo, hi } // padTimestamp left-pads timestamp with zeros to userHistoryTimestampWidth so // that lexicographic ordering of the resulting strings matches numeric order. func padTimestamp(timestamp int64) string { s := strconv.FormatInt(timestamp, 10) if len(s) >= userHistoryTimestampWidth { return s } pad := make([]byte, userHistoryTimestampWidth-len(s)) for i := range pad { pad[i] = '0' } return string(pad) + s } // parseUserHistoryKey splits a composite key produced by makeUserHistoryKey back into its address and timestamp components. func parseUserHistoryKey(key string) (addrStr string, timestamp int64, ok bool) { for i := len(key) - 1; i >= 0; i-- { if key[i] == userHistoryKeySeparator[0] { ts, err := strconv.ParseInt(key[i+1:], 10, 64) if err != nil { return "", 0, false } return key[:i], ts, true } } return "", 0, false }