/
utxoindex.go
executable file
·202 lines (163 loc) · 5.27 KB
/
utxoindex.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package utxoindex
import (
"sync"
"github.com/Nexellia-Network/nexelliad/domain"
"github.com/Nexellia-Network/nexelliad/domain/consensus/model/externalapi"
"github.com/Nexellia-Network/nexelliad/infrastructure/db/database"
"github.com/Nexellia-Network/nexelliad/infrastructure/logger"
)
// UTXOIndex maintains an index between transaction scriptPublicKeys
// and UTXOs
type UTXOIndex struct {
domain domain.Domain
store *utxoIndexStore
mutex sync.Mutex
}
// New creates a new UTXO index.
//
// NOTE: While this is called no new blocks can be added to the consensus.
func New(domain domain.Domain, database database.Database) (*UTXOIndex, error) {
utxoIndex := &UTXOIndex{
domain: domain,
store: newUTXOIndexStore(database),
}
isSynced, err := utxoIndex.isSynced()
if err != nil {
return nil, err
}
///Has check is for migration to circulating supply, can be removed eventually.
hasCirculatingSupplyKey, err := utxoIndex.store.database.Has(circulatingSupplyKey)
if err != nil {
return nil, err
}
if !isSynced || !hasCirculatingSupplyKey {
err := utxoIndex.Reset()
if err != nil {
return nil, err
}
}
return utxoIndex, nil
}
// Reset deletes the whole UTXO index and resyncs it from consensus.
func (ui *UTXOIndex) Reset() error {
ui.mutex.Lock()
defer ui.mutex.Unlock()
err := ui.store.deleteAll()
if err != nil {
return err
}
virtualInfo, err := ui.domain.Consensus().GetVirtualInfo()
if err != nil {
return err
}
err = ui.store.initializeCirculatingSompiSupply() //At this point the database is empty, so the sole purpose of this call is to initialize the circulating supply key
if err != nil {
return err
}
var fromOutpoint *externalapi.DomainOutpoint
for {
const step = 1000
virtualUTXOs, err := ui.domain.Consensus().GetVirtualUTXOs(virtualInfo.ParentHashes, fromOutpoint, step)
if err != nil {
return err
}
err = ui.store.addAndCommitOutpointsWithoutTransaction(virtualUTXOs)
if err != nil {
return err
}
if len(virtualUTXOs) < step {
break
}
fromOutpoint = virtualUTXOs[len(virtualUTXOs)-1].Outpoint
}
// This has to be done last to mark that the reset went smoothly and no reset has to be called next time.
return ui.store.updateAndCommitVirtualParentsWithoutTransaction(virtualInfo.ParentHashes)
}
func (ui *UTXOIndex) isSynced() (bool, error) {
utxoIndexVirtualParents, err := ui.store.getVirtualParents()
if err != nil {
if database.IsNotFoundError(err) {
return false, nil
}
return false, err
}
virtualInfo, err := ui.domain.Consensus().GetVirtualInfo()
if err != nil {
return false, err
}
return externalapi.HashesEqual(virtualInfo.ParentHashes, utxoIndexVirtualParents), nil
}
// Update updates the UTXO index with the given DAG selected parent chain changes
func (ui *UTXOIndex) Update(virtualChangeSet *externalapi.VirtualChangeSet) (*UTXOChanges, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "UTXOIndex.Update")
defer onEnd()
ui.mutex.Lock()
defer ui.mutex.Unlock()
log.Tracef("Updating UTXO index with VirtualUTXODiff: %+v", virtualChangeSet.VirtualUTXODiff)
err := ui.removeUTXOs(virtualChangeSet.VirtualUTXODiff.ToRemove())
if err != nil {
return nil, err
}
err = ui.addUTXOs(virtualChangeSet.VirtualUTXODiff.ToAdd())
if err != nil {
return nil, err
}
ui.store.updateVirtualParents(virtualChangeSet.VirtualParents)
added, removed, _ := ui.store.stagedData()
utxoIndexChanges := &UTXOChanges{
Added: added,
Removed: removed,
}
err = ui.store.commit()
if err != nil {
return nil, err
}
log.Tracef("UTXO index updated with the UTXOChanged: %+v", utxoIndexChanges)
return utxoIndexChanges, nil
}
func (ui *UTXOIndex) addUTXOs(toAdd externalapi.UTXOCollection) error {
iterator := toAdd.Iterator()
defer iterator.Close()
for ok := iterator.First(); ok; ok = iterator.Next() {
outpoint, entry, err := iterator.Get()
if err != nil {
return err
}
log.Tracef("Adding outpoint %s to UTXO index", outpoint)
err = ui.store.add(entry.ScriptPublicKey(), outpoint, entry)
if err != nil {
return err
}
}
return nil
}
func (ui *UTXOIndex) removeUTXOs(toRemove externalapi.UTXOCollection) error {
iterator := toRemove.Iterator()
defer iterator.Close()
for ok := iterator.First(); ok; ok = iterator.Next() {
outpoint, entry, err := iterator.Get()
if err != nil {
return err
}
log.Tracef("Removing outpoint %s from UTXO index", outpoint)
err = ui.store.remove(entry.ScriptPublicKey(), outpoint, entry)
if err != nil {
return err
}
}
return nil
}
// UTXOs returns all the UTXOs for the given scriptPublicKey
func (ui *UTXOIndex) UTXOs(scriptPublicKey *externalapi.ScriptPublicKey) (UTXOOutpointEntryPairs, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "UTXOIndex.UTXOs")
defer onEnd()
ui.mutex.Lock()
defer ui.mutex.Unlock()
return ui.store.getUTXOOutpointEntryPairs(scriptPublicKey)
}
// GetCirculatingSompiSupply returns the current circulating supply of sompis in the network
func (ui *UTXOIndex) GetCirculatingSompiSupply() (uint64, error) {
ui.mutex.Lock()
defer ui.mutex.Unlock()
return ui.store.getCirculatingSompiSupply()
}