mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-13 13:00:10 +00:00

* [NOD-855] Save ECMH for each block UTXO * [NOD-855] Remove UpdateExtraNonce method * [NOD-855] Remove multiset data from UTXO diffs * [NOD-855] Fix to fetch multiset of selected parent * [NOD-855] Don't remove coinbase inputs from multiset * [NOD-855] Create multisetBucketName on startup * [NOD-855] Remove multiset from UTXO diff tests * [NOD-855] clear new entries from multisetstore on saveChangesFromBlock * [NOD-855] Fix tests * [NOD-855] Use UnacceptedBlueScore when adding current block transactions to multiset * [NOD-855] Hash utxo before adding it to multiset * [NOD-855] Pass isCoinbase to NewUTXOEntry * [NOD-855] Do not use hash when adding entries to multiset * [NOD-855] When calculating multiset, replace the unaccepted blue score of selected parent transaction with the block blue score * [NOD-855] Manually add a chained transaction to a block in TestChainedTransactions * [NOD-855] Change name and comments * [NOD-855] Use FindAcceptanceData to find a specific block acceptance data * [NOD-855] Remove redundant copy of txIn.PreviousOutpoint * [NOD-855] Use fmt.Sprintf when creating internalRPCError
207 lines
5.8 KiB
Go
207 lines
5.8 KiB
Go
package blockdag
|
|
|
|
import (
|
|
"bytes"
|
|
"github.com/kaspanet/kaspad/database"
|
|
"github.com/kaspanet/kaspad/util/daghash"
|
|
"github.com/kaspanet/kaspad/util/locks"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
// Allocate a buffer here to avoid needless allocations/grows
|
|
// while writing each entry.
|
|
buffer := &bytes.Buffer{}
|
|
for hash := range diffStore.dirty {
|
|
hash := hash // Copy hash to a new variable to avoid passing the same pointer
|
|
buffer.Reset()
|
|
diffData := diffStore.loaded[hash]
|
|
err := dbStoreDiffData(dbTx, buffer, &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, writeBuffer *bytes.Buffer, hash *daghash.Hash, diffData *blockUTXODiffData) error {
|
|
// To avoid a ton of allocs, use the given writeBuffer
|
|
// instead of allocating one. We expect the buffer to
|
|
// already be initalized and, in most cases, to already
|
|
// be large enough to accommodate the serialized data
|
|
// without growing.
|
|
err := serializeBlockUTXODiffData(writeBuffer, diffData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Bucket.Put doesn't copy on its own, so we manually
|
|
// copy here. We do so because we expect the buffer
|
|
// to be reused once we're done with it.
|
|
serializedDiffData := make([]byte, writeBuffer.Len())
|
|
copy(serializedDiffData, writeBuffer.Bytes())
|
|
|
|
return dbTx.Metadata().Bucket(utxoDiffsBucketName).Put(hash[:], serializedDiffData)
|
|
}
|
|
|
|
func dbRemoveDiffData(dbTx database.Tx, hash *daghash.Hash) error {
|
|
return dbTx.Metadata().Bucket(utxoDiffsBucketName).Delete(hash[:])
|
|
}
|