Files
utxo/utxo_fetching.go
zeekayandHanzo Dev 6efa1285f9 lint: clean all 21 staticcheck findings — CI green, drop last codec reference
- SA1019: math.Mul64/Add64 -> generic math.Mul/Add (ed25519fx, mldsafx,
  schnorrfx inputs; flow_checker; utxo_fetching); prefixdb.NewNested -> New (alias)
- unused: DELETE dead codecVersion const (last codec reference in utxo — full
  ZAP native now), isSortedAndUniqueOrdered (root + secp256k1fx), mldsafx
  Keychain.get, and all 6 redundant TransferOutput.isState() markers (the
  embedded verify.IsState already provides State membership)
- QF1008: drop embedded-field selectors (utxo_wire, ed25519fx, nftfx,
  propertyfx + tests); ST1005: lowercase Schnorr error string

Behavior-identical; 11/11 packages pass.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-12 22:32:45 -07:00

110 lines
2.9 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package utxo
import (
"bytes"
"fmt"
"math"
"github.com/luxfi/ids"
"github.com/luxfi/math/set"
safemath "github.com/luxfi/math"
)
// GetBalance returns the current balance of [addrs]
func GetBalance(db UTXOReader, addrs set.Set[ids.ShortID]) (uint64, error) {
utxos, err := GetAllUTXOs(db, addrs)
if err != nil {
return 0, fmt.Errorf("couldn't get UTXOs: %w", err)
}
balance := uint64(0)
for _, utxo := range utxos {
if out, ok := utxo.Out.(Amounter); ok {
balance, err = safemath.Add(out.Amount(), balance)
if err != nil {
return 0, err
}
}
}
return balance, nil
}
func GetAllUTXOs(db UTXOReader, addrs set.Set[ids.ShortID]) ([]*UTXO, error) {
utxos, _, _, err := GetPaginatedUTXOs(
db,
addrs,
ids.ShortEmpty,
ids.Empty,
math.MaxInt,
)
return utxos, err
}
// GetPaginatedUTXOs returns UTXOs such that at least one of the addresses in
// [addrs] is referenced.
//
// Returns at most [limit] UTXOs.
//
// Only returns UTXOs associated with addresses >= [startAddr].
//
// For address [startAddr], only returns UTXOs whose IDs are greater than
// [startUTXOID].
//
// Returns:
// * The fetched UTXOs
// * The address associated with the last UTXO fetched
// * The ID of the last UTXO fetched
func GetPaginatedUTXOs(
db UTXOReader,
addrs set.Set[ids.ShortID],
lastAddr ids.ShortID,
lastUTXOID ids.ID,
limit int,
) ([]*UTXO, ids.ShortID, ids.ID, error) {
var (
utxos []*UTXO
seen = set.NewSet[ids.ID](limit) // IDs of UTXOs already in the list
searchSize = limit // the limit diminishes which can impact the expected return
addrsList = addrs.List()
)
sortByCompare(addrsList) // enforces the same ordering for pagination
for _, addr := range addrsList {
start := ids.Empty
if comp := bytes.Compare(addr.Bytes(), lastAddr.Bytes()); comp == -1 { // Skip addresses before [startAddr]
continue
} else if comp == 0 {
start = lastUTXOID
}
lastAddr = addr // The last address searched
utxoIDs, err := db.UTXOIDs(addr.Bytes(), start, searchSize) // Get UTXOs associated with [addr]
if err != nil {
return nil, ids.ShortID{}, ids.Empty, fmt.Errorf("couldn't get UTXOs for address %s: %w", addr, err)
}
for _, utxoID := range utxoIDs {
lastUTXOID = utxoID // The last searched UTXO - not the last found
if seen.Contains(utxoID) { // Already have this UTXO in the list
continue
}
utxo, err := db.GetUTXO(utxoID)
if err != nil {
return nil, ids.ShortID{}, ids.Empty, fmt.Errorf("couldn't get UTXO %s: %w", utxoID, err)
}
utxos = append(utxos, utxo)
seen.Add(utxoID)
limit--
if limit <= 0 {
return utxos, lastAddr, lastUTXOID, nil // Found [limit] utxos; stop.
}
}
}
return utxos, lastAddr, lastUTXOID, nil // Didn't reach the [limit] utxos; no more were found
}