-
Notifications
You must be signed in to change notification settings - Fork 665
/
state.go
150 lines (127 loc) · 4.24 KB
/
state.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package platformvm
import (
"bytes"
"fmt"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/vms/components/avax"
safemath "github.com/ava-labs/avalanchego/utils/math"
)
// getPaginatedUTXOs returns UTXOs such that at least one of the addresses in
// [addrs] is referenced.
// Returns at most [limit] UTXOs.
// If [limit] <= 0 or [limit] > maxUTXOsToFetch, it is set to [maxUTXOsToFetch].
// 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 (vm *VM) getPaginatedUTXOs(
addrs ids.ShortSet,
startAddr ids.ShortID,
startUTXOID ids.ID,
limit int,
) ([]*avax.UTXO, ids.ShortID, ids.ID, error) {
if limit <= 0 || limit > maxUTXOsToFetch {
limit = maxUTXOsToFetch
}
lastAddr := ids.ShortEmpty
lastIndex := ids.Empty
utxos := make([]*avax.UTXO, 0, limit)
seen := make(ids.Set, limit) // IDs of UTXOs already in the list
searchSize := limit // the limit diminishes which can impact the expected return
// enforces the same ordering for pagination
addrsList := addrs.List()
ids.SortShortIDs(addrsList)
for _, addr := range addrsList {
start := ids.Empty
if comp := bytes.Compare(addr.Bytes(), startAddr.Bytes()); comp == -1 { // Skip addresses before [startAddr]
continue
} else if comp == 0 {
start = startUTXOID
}
utxoIDs, err := vm.internalState.UTXOIDs(addr.Bytes(), start, searchSize) // Get UTXOs associated with [addr]
if err != nil {
return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("couldn't get UTXOs for address %s: %w", addr, err)
}
for _, utxoID := range utxoIDs {
lastIndex = utxoID // The last searched UTXO - not the last found
lastAddr = addr // The last address searched that has UTXOs (even duplicated) - not the last found
if seen.Contains(utxoID) { // Already have this UTXO in the list
continue
}
utxo, err := vm.internalState.GetUTXO(utxoID)
if err != nil {
return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("couldn't get UTXO %s: %w", utxoID, err)
}
utxos = append(utxos, utxo)
seen.Add(utxoID)
limit--
if limit <= 0 {
return utxos, lastAddr, lastIndex, nil // Found [limit] utxos; stop.
}
}
}
return utxos, lastAddr, lastIndex, nil // Didnt reach the [limit] utxos; no more were found
}
func (vm *VM) getAllUTXOs(
addrs ids.ShortSet,
) ([]*avax.UTXO, error) {
var err error
seen := make(ids.Set, maxUTXOsToFetch) // IDs of UTXOs already in the list
utxos := make([]*avax.UTXO, 0, maxUTXOsToFetch)
// enforces the same ordering for pagination
addrsList := addrs.List()
ids.SortShortIDs(addrsList)
// iterate over the addresses and get all the utxos
for _, addr := range addrsList {
_, err = vm.getAllUniqueAddressUTXOs(addr, &seen, &utxos)
if err != nil {
return nil, fmt.Errorf("couldn't get UTXOs for address %s: %w", addr, err)
}
}
return utxos, nil
}
func (vm *VM) getAllUniqueAddressUTXOs(addr ids.ShortID, seen *ids.Set, utxos *[]*avax.UTXO) (ids.ID, error) {
lastIndex := ids.Empty
for {
utxoIDs, err := vm.internalState.UTXOIDs(addr.Bytes(), lastIndex, maxUTXOsToFetch) // Get UTXOs associated with [addr]
if err != nil {
return ids.ID{}, err
}
if len(utxoIDs) == 0 {
return lastIndex, nil
}
for _, utxoID := range utxoIDs {
lastIndex = utxoID // The last searched UTXO - not the last found
if seen.Contains(utxoID) { // Already have this UTXO in the list
continue
}
utxo, err := vm.internalState.GetUTXO(utxoID)
if err != nil {
return ids.ID{}, err
}
*utxos = append(*utxos, utxo)
seen.Add(utxoID)
}
}
}
// getBalance returns the current balance of [addrs]
func (vm *VM) getBalance(addrs ids.ShortSet) (uint64, error) {
utxos, err := vm.getAllUTXOs(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.(avax.Amounter); ok {
if balance, err = safemath.Add64(out.Amount(), balance); err != nil {
return 0, err
}
}
}
return balance, nil
}