mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00

* [NOD-375] Move to pkg/errors * [NOD-375] Fix tests * [NOD-375] Make AreErrorsEqual a shared function
192 lines
5.1 KiB
Go
192 lines
5.1 KiB
Go
package blockdag
|
|
|
|
import (
|
|
"github.com/daglabs/btcd/database"
|
|
"github.com/daglabs/btcd/util/daghash"
|
|
"github.com/daglabs/btcd/util/locks"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var multisetPointSize = 32
|
|
|
|
type blockUTXODiffData struct {
|
|
diff *UTXODiff
|
|
diffChild *blockNode
|
|
}
|
|
|
|
type utxoDiffStore struct {
|
|
dag *BlockDAG
|
|
dirty map[daghash.Hash]struct{}
|
|
loaded map[daghash.Hash]*blockUTXODiffData
|
|
mtx *locks.PriorityMutex
|
|
}
|
|
|
|
func newUTXODiffStore(dag *BlockDAG) *utxoDiffStore {
|
|
return &utxoDiffStore{
|
|
dag: dag,
|
|
dirty: make(map[daghash.Hash]struct{}),
|
|
loaded: make(map[daghash.Hash]*blockUTXODiffData),
|
|
mtx: locks.NewPriorityMutex(),
|
|
}
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) setBlockDiff(node *blockNode, diff *UTXODiff) error {
|
|
diffStore.mtx.HighPriorityWriteLock()
|
|
defer diffStore.mtx.HighPriorityWriteUnlock()
|
|
// load the diff data from DB to diffStore.loaded
|
|
_, exists, err := diffStore.diffDataByHash(node.hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
diffStore.loaded[*node.hash] = &blockUTXODiffData{}
|
|
}
|
|
|
|
diffStore.loaded[*node.hash].diff = diff
|
|
diffStore.setBlockAsDirty(node.hash)
|
|
return nil
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) setBlockDiffChild(node *blockNode, diffChild *blockNode) error {
|
|
diffStore.mtx.HighPriorityWriteLock()
|
|
defer diffStore.mtx.HighPriorityWriteUnlock()
|
|
// load the diff data from DB to diffStore.loaded
|
|
_, exists, err := diffStore.diffDataByHash(node.hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
return diffNotFoundError(node)
|
|
}
|
|
|
|
diffStore.loaded[*node.hash].diffChild = diffChild
|
|
diffStore.setBlockAsDirty(node.hash)
|
|
return nil
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) removeBlocksDiffData(dbTx database.Tx, blockHashes []*daghash.Hash) error {
|
|
for _, hash := range blockHashes {
|
|
err := diffStore.removeBlockDiffData(dbTx, hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) removeBlockDiffData(dbTx database.Tx, blockHash *daghash.Hash) error {
|
|
diffStore.mtx.LowPriorityWriteLock()
|
|
defer diffStore.mtx.LowPriorityWriteUnlock()
|
|
delete(diffStore.loaded, *blockHash)
|
|
err := dbRemoveDiffData(dbTx, blockHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) setBlockAsDirty(blockHash *daghash.Hash) {
|
|
diffStore.dirty[*blockHash] = struct{}{}
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) diffDataByHash(hash *daghash.Hash) (*blockUTXODiffData, bool, error) {
|
|
if diffData, ok := diffStore.loaded[*hash]; ok {
|
|
return diffData, true, nil
|
|
}
|
|
diffData, err := diffStore.diffDataFromDB(hash)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
exists := diffData != nil
|
|
if exists {
|
|
diffStore.loaded[*hash] = diffData
|
|
}
|
|
return diffData, exists, nil
|
|
}
|
|
|
|
func diffNotFoundError(node *blockNode) error {
|
|
return errors.Errorf("Couldn't find diff data for block %s", node.hash)
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) diffByNode(node *blockNode) (*UTXODiff, error) {
|
|
diffStore.mtx.HighPriorityReadLock()
|
|
defer diffStore.mtx.HighPriorityReadUnlock()
|
|
diffData, exists, err := diffStore.diffDataByHash(node.hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !exists {
|
|
return nil, diffNotFoundError(node)
|
|
}
|
|
return diffData.diff, nil
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) diffChildByNode(node *blockNode) (*blockNode, error) {
|
|
diffStore.mtx.HighPriorityReadLock()
|
|
defer diffStore.mtx.HighPriorityReadUnlock()
|
|
diffData, exists, err := diffStore.diffDataByHash(node.hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !exists {
|
|
return nil, diffNotFoundError(node)
|
|
}
|
|
return diffData.diffChild, nil
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) diffDataFromDB(hash *daghash.Hash) (*blockUTXODiffData, error) {
|
|
var diffData *blockUTXODiffData
|
|
err := diffStore.dag.db.View(func(dbTx database.Tx) error {
|
|
bucket := dbTx.Metadata().Bucket(utxoDiffsBucketName)
|
|
serializedBlockDiffData := bucket.Get(hash[:])
|
|
if serializedBlockDiffData != nil {
|
|
var err error
|
|
diffData, err = diffStore.deserializeBlockUTXODiffData(serializedBlockDiffData)
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return diffData, nil
|
|
}
|
|
|
|
// flushToDB writes all dirty diff data to the database. If all writes
|
|
// succeed, this clears the dirty set.
|
|
func (diffStore *utxoDiffStore) flushToDB(dbTx database.Tx) error {
|
|
diffStore.mtx.HighPriorityWriteLock()
|
|
defer diffStore.mtx.HighPriorityWriteUnlock()
|
|
if len(diffStore.dirty) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for hash := range diffStore.dirty {
|
|
diffData := diffStore.loaded[hash]
|
|
err := dbStoreDiffData(dbTx, &hash, diffData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (diffStore *utxoDiffStore) clearDirtyEntries() {
|
|
diffStore.dirty = make(map[daghash.Hash]struct{})
|
|
}
|
|
|
|
// dbStoreDiffData stores the UTXO diff data to the database.
|
|
// This overwrites the current entry if there exists one.
|
|
func dbStoreDiffData(dbTx database.Tx, hash *daghash.Hash, diffData *blockUTXODiffData) error {
|
|
serializedDiffData, err := serializeBlockUTXODiffData(diffData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return dbTx.Metadata().Bucket(utxoDiffsBucketName).Put(hash[:], serializedDiffData)
|
|
}
|
|
|
|
func dbRemoveDiffData(dbTx database.Tx, hash *daghash.Hash) error {
|
|
return dbTx.Metadata().Bucket(utxoDiffsBucketName).Delete(hash[:])
|
|
}
|