mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00

* Add Close() function to all the iterators * Add defer iterator.Close() whenever we open an iterator * Add isClosed to all iterators and panic/return error if used after closing Co-authored-by: Svarog <feanorr@gmail.com>
178 lines
4.3 KiB
Go
178 lines
4.3 KiB
Go
package utxoindex
|
|
|
|
import (
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
|
"sync"
|
|
)
|
|
|
|
// UTXOIndex maintains an index between transaction scriptPublicKeys
|
|
// and UTXOs
|
|
type UTXOIndex struct {
|
|
consensus externalapi.Consensus
|
|
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(consensus externalapi.Consensus, database database.Database) (*UTXOIndex, error) {
|
|
utxoIndex := &UTXOIndex{
|
|
consensus: consensus,
|
|
store: newUTXOIndexStore(database),
|
|
}
|
|
|
|
isSynced, err := utxoIndex.isSynced()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !isSynced {
|
|
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 {
|
|
err := ui.store.deleteAll()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
virtualInfo, err := ui.consensus.GetVirtualInfo()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var fromOutpoint *externalapi.DomainOutpoint
|
|
for {
|
|
const step = 1000
|
|
virtualUTXOs, err := ui.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.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(blockInsertionResult *externalapi.BlockInsertionResult) (*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", blockInsertionResult.VirtualUTXODiff)
|
|
err := ui.removeUTXOs(blockInsertionResult.VirtualUTXODiff.ToRemove())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = ui.addUTXOs(blockInsertionResult.VirtualUTXODiff.ToAdd())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ui.store.updateVirtualParents(blockInsertionResult.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)
|
|
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)
|
|
}
|