mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00
[NOD-855] Save ECMH for block utxo and not diff utxo (#669)
* [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
This commit is contained in:
parent
1020402b34
commit
d83862f36c
204
blockdag/dag.go
204
blockdag/dag.go
@ -6,6 +6,7 @@ package blockdag
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kaspanet/kaspad/ecc"
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
@ -153,6 +154,7 @@ type BlockDAG struct {
|
|||||||
SubnetworkStore *SubnetworkStore
|
SubnetworkStore *SubnetworkStore
|
||||||
utxoDiffStore *utxoDiffStore
|
utxoDiffStore *utxoDiffStore
|
||||||
reachabilityStore *reachabilityStore
|
reachabilityStore *reachabilityStore
|
||||||
|
multisetStore *multisetStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsKnownBlock returns whether or not the DAG instance has the block represented
|
// IsKnownBlock returns whether or not the DAG instance has the block represented
|
||||||
@ -560,14 +562,13 @@ func (dag *BlockDAG) connectBlock(node *blockNode,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newBlockUTXO, txsAcceptanceData, newBlockFeeData, err := node.verifyAndBuildUTXO(dag, block.Transactions(), fastAdd)
|
newBlockUTXO, txsAcceptanceData, newBlockFeeData, newBlockMultiSet, err := node.verifyAndBuildUTXO(dag, block.Transactions(), fastAdd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newErrString := fmt.Sprintf("error verifying UTXO for %s: %s", node, err)
|
|
||||||
var ruleErr RuleError
|
var ruleErr RuleError
|
||||||
if ok := errors.As(err, &ruleErr); ok {
|
if ok := errors.As(err, &ruleErr); ok {
|
||||||
return nil, ruleError(ruleErr.ErrorCode, newErrString)
|
return nil, ruleError(ruleErr.ErrorCode, fmt.Sprintf("error verifying UTXO for %s: %s", node, err))
|
||||||
}
|
}
|
||||||
return nil, errors.New(newErrString)
|
return nil, errors.Wrapf(err, "error verifying UTXO for %s", node)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = node.validateCoinbaseTransaction(dag, block, txsAcceptanceData)
|
err = node.validateCoinbaseTransaction(dag, block, txsAcceptanceData)
|
||||||
@ -576,7 +577,7 @@ func (dag *BlockDAG) connectBlock(node *blockNode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply all changes to the DAG.
|
// Apply all changes to the DAG.
|
||||||
virtualUTXODiff, virtualTxsAcceptanceData, chainUpdates, err := dag.applyDAGChanges(node, newBlockUTXO, selectedParentAnticone)
|
virtualUTXODiff, virtualTxsAcceptanceData, chainUpdates, err := dag.applyDAGChanges(node, newBlockUTXO, newBlockMultiSet, selectedParentAnticone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Since all validation logic has already ran, if applyDAGChanges errors out,
|
// Since all validation logic has already ran, if applyDAGChanges errors out,
|
||||||
// this means we have a problem in the internal structure of the DAG - a problem which is
|
// this means we have a problem in the internal structure of the DAG - a problem which is
|
||||||
@ -593,6 +594,126 @@ func (dag *BlockDAG) connectBlock(node *blockNode,
|
|||||||
return chainUpdates, nil
|
return chainUpdates, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calcMultiset returns the multiset of the UTXO of the given block with the given transactions.
|
||||||
|
func (node *blockNode) calcMultiset(dag *BlockDAG, transactions []*util.Tx, acceptanceData MultiBlockTxsAcceptanceData, selectedParentUTXO, pastUTXO UTXOSet) (*ecc.Multiset, error) {
|
||||||
|
ms, err := node.pastUTXOMultiSet(dag, acceptanceData, selectedParentUTXO)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tx := range transactions {
|
||||||
|
ms, err = addTxToMultiset(ms, tx.MsgTx(), pastUTXO, UnacceptedBlueScore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptedSelectedParentMultiset takes the multiset of the selected
|
||||||
|
// parent, replaces all the selected parent outputs' blue score with
|
||||||
|
// the block blue score and returns the result.
|
||||||
|
func (node *blockNode) acceptedSelectedParentMultiset(dag *BlockDAG,
|
||||||
|
acceptanceData MultiBlockTxsAcceptanceData) (*ecc.Multiset, error) {
|
||||||
|
|
||||||
|
if node.isGenesis() {
|
||||||
|
return ecc.NewMultiset(ecc.S256()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ms, err := dag.multisetStore.multisetByBlockNode(node.selectedParent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedParentAcceptanceData, exists := acceptanceData.FindAcceptanceData(node.selectedParent.hash)
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.Errorf("couldn't find selected parent acceptance data for block %s", node)
|
||||||
|
}
|
||||||
|
for _, txAcceptanceData := range selectedParentAcceptanceData.TxAcceptanceData {
|
||||||
|
tx := txAcceptanceData.Tx
|
||||||
|
msgTx := tx.MsgTx()
|
||||||
|
isCoinbase := tx.IsCoinBase()
|
||||||
|
for i, txOut := range msgTx.TxOut {
|
||||||
|
outpoint := *wire.NewOutpoint(tx.ID(), uint32(i))
|
||||||
|
|
||||||
|
unacceptedEntry := NewUTXOEntry(txOut, isCoinbase, UnacceptedBlueScore)
|
||||||
|
acceptedEntry := NewUTXOEntry(txOut, isCoinbase, node.blueScore)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ms, err = removeUTXOFromMultiset(ms, unacceptedEntry, &outpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ms, err = addUTXOToMultiset(ms, acceptedEntry, &outpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *blockNode) pastUTXOMultiSet(dag *BlockDAG, acceptanceData MultiBlockTxsAcceptanceData, selectedParentUTXO UTXOSet) (*ecc.Multiset, error) {
|
||||||
|
ms, err := node.acceptedSelectedParentMultiset(dag, acceptanceData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, blockAcceptanceData := range acceptanceData {
|
||||||
|
if blockAcceptanceData.BlockHash.IsEqual(node.selectedParent.hash) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, txAcceptanceData := range blockAcceptanceData.TxAcceptanceData {
|
||||||
|
if !txAcceptanceData.IsAccepted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := txAcceptanceData.Tx.MsgTx()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ms, err = addTxToMultiset(ms, tx, selectedParentUTXO, node.blueScore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addTxToMultiset(ms *ecc.Multiset, tx *wire.MsgTx, pastUTXO UTXOSet, blockBlueScore uint64) (*ecc.Multiset, error) {
|
||||||
|
isCoinbase := tx.IsCoinBase()
|
||||||
|
if !isCoinbase {
|
||||||
|
for _, txIn := range tx.TxIn {
|
||||||
|
entry, ok := pastUTXO.Get(txIn.PreviousOutpoint)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("Couldn't find entry for outpoint %s", txIn.PreviousOutpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ms, err = removeUTXOFromMultiset(ms, entry, &txIn.PreviousOutpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, txOut := range tx.TxOut {
|
||||||
|
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
|
||||||
|
entry := NewUTXOEntry(txOut, isCoinbase, blockBlueScore)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ms, err = addUTXOToMultiset(ms, entry, &outpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ms, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dag *BlockDAG) saveChangesFromBlock(block *util.Block, virtualUTXODiff *UTXODiff,
|
func (dag *BlockDAG) saveChangesFromBlock(block *util.Block, virtualUTXODiff *UTXODiff,
|
||||||
txsAcceptanceData MultiBlockTxsAcceptanceData, virtualTxsAcceptanceData MultiBlockTxsAcceptanceData,
|
txsAcceptanceData MultiBlockTxsAcceptanceData, virtualTxsAcceptanceData MultiBlockTxsAcceptanceData,
|
||||||
feeData compactFeeData) error {
|
feeData compactFeeData) error {
|
||||||
@ -614,6 +735,11 @@ func (dag *BlockDAG) saveChangesFromBlock(block *util.Block, virtualUTXODiff *UT
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = dag.multisetStore.flushToDB(dbTx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Update best block state.
|
// Update best block state.
|
||||||
state := &dagState{
|
state := &dagState{
|
||||||
TipHashes: dag.TipHashes(),
|
TipHashes: dag.TipHashes(),
|
||||||
@ -663,6 +789,7 @@ func (dag *BlockDAG) saveChangesFromBlock(block *util.Block, virtualUTXODiff *UT
|
|||||||
dag.index.clearDirtyEntries()
|
dag.index.clearDirtyEntries()
|
||||||
dag.utxoDiffStore.clearDirtyEntries()
|
dag.utxoDiffStore.clearDirtyEntries()
|
||||||
dag.reachabilityStore.clearDirtyEntries()
|
dag.reachabilityStore.clearDirtyEntries()
|
||||||
|
dag.multisetStore.clearNewEntries()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -839,7 +966,7 @@ func (dag *BlockDAG) NextAcceptedIDMerkleRootNoLock() (*daghash.Hash, error) {
|
|||||||
//
|
//
|
||||||
// This function MUST be called with the DAG read-lock held
|
// This function MUST be called with the DAG read-lock held
|
||||||
func (dag *BlockDAG) TxsAcceptedByVirtual() (MultiBlockTxsAcceptanceData, error) {
|
func (dag *BlockDAG) TxsAcceptedByVirtual() (MultiBlockTxsAcceptanceData, error) {
|
||||||
_, txsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode)
|
_, _, txsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode)
|
||||||
return txsAcceptanceData, err
|
return txsAcceptanceData, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -851,7 +978,7 @@ func (dag *BlockDAG) TxsAcceptedByBlockHash(blockHash *daghash.Hash) (MultiBlock
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return nil, errors.Errorf("Couldn't find block %s", blockHash)
|
return nil, errors.Errorf("Couldn't find block %s", blockHash)
|
||||||
}
|
}
|
||||||
_, txsAcceptanceData, err := dag.pastUTXO(node)
|
_, _, txsAcceptanceData, err := dag.pastUTXO(node)
|
||||||
return txsAcceptanceData, err
|
return txsAcceptanceData, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,12 +989,13 @@ func (dag *BlockDAG) TxsAcceptedByBlockHash(blockHash *daghash.Hash) (MultiBlock
|
|||||||
// 4. Updates each of the tips' utxoDiff.
|
// 4. Updates each of the tips' utxoDiff.
|
||||||
// 5. Applies the new virtual's blue score to all the unaccepted UTXOs
|
// 5. Applies the new virtual's blue score to all the unaccepted UTXOs
|
||||||
// 6. Adds the block to the reachability structures
|
// 6. Adds the block to the reachability structures
|
||||||
// 7. Updates the finality point of the DAG (if required).
|
// 7. Adds the multiset of the block to the multiset store.
|
||||||
|
// 8. Updates the finality point of the DAG (if required).
|
||||||
//
|
//
|
||||||
// It returns the diff in the virtual block's UTXO set.
|
// It returns the diff in the virtual block's UTXO set.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the DAG state lock held (for writes).
|
// This function MUST be called with the DAG state lock held (for writes).
|
||||||
func (dag *BlockDAG) applyDAGChanges(node *blockNode, newBlockUTXO UTXOSet, selectedParentAnticone []*blockNode) (
|
func (dag *BlockDAG) applyDAGChanges(node *blockNode, newBlockUTXO UTXOSet, newBlockMultiset *ecc.Multiset, selectedParentAnticone []*blockNode) (
|
||||||
virtualUTXODiff *UTXODiff, virtualTxsAcceptanceData MultiBlockTxsAcceptanceData,
|
virtualUTXODiff *UTXODiff, virtualTxsAcceptanceData MultiBlockTxsAcceptanceData,
|
||||||
chainUpdates *chainUpdates, err error) {
|
chainUpdates *chainUpdates, err error) {
|
||||||
|
|
||||||
@ -877,6 +1005,8 @@ func (dag *BlockDAG) applyDAGChanges(node *blockNode, newBlockUTXO UTXOSet, sele
|
|||||||
return nil, nil, nil, errors.Wrap(err, "failed updating reachability")
|
return nil, nil, nil, errors.Wrap(err, "failed updating reachability")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dag.multisetStore.setMultiset(node, newBlockMultiset)
|
||||||
|
|
||||||
if err = node.updateParents(dag, newBlockUTXO); err != nil {
|
if err = node.updateParents(dag, newBlockUTXO); err != nil {
|
||||||
return nil, nil, nil, errors.Wrapf(err, "failed updating parents of %s", node)
|
return nil, nil, nil, errors.Wrapf(err, "failed updating parents of %s", node)
|
||||||
}
|
}
|
||||||
@ -885,7 +1015,7 @@ func (dag *BlockDAG) applyDAGChanges(node *blockNode, newBlockUTXO UTXOSet, sele
|
|||||||
chainUpdates = dag.virtual.AddTip(node)
|
chainUpdates = dag.virtual.AddTip(node)
|
||||||
|
|
||||||
// Build a UTXO set for the new virtual block
|
// Build a UTXO set for the new virtual block
|
||||||
newVirtualUTXO, virtualTxsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode)
|
newVirtualUTXO, _, virtualTxsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, errors.Wrap(err, "could not restore past UTXO for virtual")
|
return nil, nil, nil, errors.Wrap(err, "could not restore past UTXO for virtual")
|
||||||
}
|
}
|
||||||
@ -936,42 +1066,49 @@ func (node *blockNode) diffFromTxs(pastUTXO UTXOSet, transactions []*util.Tx) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verifyAndBuildUTXO verifies all transactions in the given block and builds its UTXO
|
// verifyAndBuildUTXO verifies all transactions in the given block and builds its UTXO
|
||||||
// to save extra traversals it returns the transactions acceptance data and the compactFeeData for the new block
|
// to save extra traversals it returns the transactions acceptance data, the compactFeeData
|
||||||
|
// for the new block and its multiset.
|
||||||
func (node *blockNode) verifyAndBuildUTXO(dag *BlockDAG, transactions []*util.Tx, fastAdd bool) (
|
func (node *blockNode) verifyAndBuildUTXO(dag *BlockDAG, transactions []*util.Tx, fastAdd bool) (
|
||||||
newBlockUTXO UTXOSet, txsAcceptanceData MultiBlockTxsAcceptanceData, newBlockFeeData compactFeeData, err error) {
|
newBlockUTXO UTXOSet, txsAcceptanceData MultiBlockTxsAcceptanceData, newBlockFeeData compactFeeData, multiset *ecc.Multiset, err error) {
|
||||||
|
|
||||||
pastUTXO, txsAcceptanceData, err := dag.pastUTXO(node)
|
pastUTXO, selectedParentUTXO, txsAcceptanceData, err := dag.pastUTXO(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = node.validateAcceptedIDMerkleRoot(dag, txsAcceptanceData)
|
err = node.validateAcceptedIDMerkleRoot(dag, txsAcceptanceData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
feeData, err := dag.checkConnectToPastUTXO(node, pastUTXO, transactions, fastAdd)
|
feeData, err := dag.checkConnectToPastUTXO(node, pastUTXO, transactions, fastAdd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
diffFromTxs, err := node.diffFromTxs(pastUTXO, transactions)
|
diffFromTxs, err := node.diffFromTxs(pastUTXO, transactions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
utxo, err := pastUTXO.WithDiff(diffFromTxs)
|
utxo, err := pastUTXO.WithDiff(diffFromTxs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
calculatedMultisetHash := utxo.Multiset().Hash()
|
multiset, err = node.calcMultiset(dag, transactions, txsAcceptanceData, selectedParentUTXO, pastUTXO)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatedMultisetHash := multiset.Hash()
|
||||||
if !calculatedMultisetHash.IsEqual(node.utxoCommitment) {
|
if !calculatedMultisetHash.IsEqual(node.utxoCommitment) {
|
||||||
str := fmt.Sprintf("block %s UTXO commitment is invalid - block "+
|
str := fmt.Sprintf("block %s UTXO commitment is invalid - block "+
|
||||||
"header indicates %s, but calculated value is %s", node.hash,
|
"header indicates %s, but calculated value is %s", node.hash,
|
||||||
node.utxoCommitment, calculatedMultisetHash)
|
node.utxoCommitment, calculatedMultisetHash)
|
||||||
return nil, nil, nil, ruleError(ErrBadUTXOCommitment, str)
|
return nil, nil, nil, nil, ruleError(ErrBadUTXOCommitment, str)
|
||||||
}
|
}
|
||||||
return utxo, txsAcceptanceData, feeData, nil
|
|
||||||
|
return utxo, txsAcceptanceData, feeData, multiset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxAcceptanceData stores a transaction together with an indication
|
// TxAcceptanceData stores a transaction together with an indication
|
||||||
@ -1125,28 +1262,33 @@ func (node *blockNode) updateParentsDiffs(dag *BlockDAG, newBlockUTXO UTXOSet) e
|
|||||||
// To save traversals over the blue blocks, it also returns the transaction acceptance data for
|
// To save traversals over the blue blocks, it also returns the transaction acceptance data for
|
||||||
// all blue blocks
|
// all blue blocks
|
||||||
func (dag *BlockDAG) pastUTXO(node *blockNode) (
|
func (dag *BlockDAG) pastUTXO(node *blockNode) (
|
||||||
pastUTXO UTXOSet, bluesTxsAcceptanceData MultiBlockTxsAcceptanceData, err error) {
|
pastUTXO, selectedParentUTXO UTXOSet, bluesTxsAcceptanceData MultiBlockTxsAcceptanceData, err error) {
|
||||||
|
|
||||||
if node.isGenesis() {
|
if node.isGenesis() {
|
||||||
return genesisPastUTXO(dag.virtual), MultiBlockTxsAcceptanceData{}, nil
|
return genesisPastUTXO(dag.virtual), NewFullUTXOSet(), MultiBlockTxsAcceptanceData{}, nil
|
||||||
}
|
}
|
||||||
selectedParentUTXO, err := dag.restoreUTXO(node.selectedParent)
|
selectedParentUTXO, err = dag.restoreUTXO(node.selectedParent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
blueBlocks, err := node.fetchBlueBlocks(dag.db)
|
blueBlocks, err := node.fetchBlueBlocks(dag.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedParent := blueBlocks[0]
|
selectedParent := blueBlocks[0]
|
||||||
acceptedSelectedParentUTXO, selectedParentAcceptanceData, err := node.acceptSelectedParentTransactions(selectedParent, selectedParentUTXO)
|
acceptedSelectedParentUTXO, selectedParentAcceptanceData, err := node.acceptSelectedParentTransactions(selectedParent, selectedParentUTXO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return node.applyBlueBlocks(acceptedSelectedParentUTXO, selectedParentAcceptanceData, blueBlocks)
|
pastUTXO, bluesTxsAcceptanceData, err = node.applyBlueBlocks(acceptedSelectedParentUTXO, selectedParentAcceptanceData, blueBlocks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pastUTXO, selectedParentUTXO, bluesTxsAcceptanceData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *blockNode) acceptSelectedParentTransactions(selectedParent *util.Block, selectedParentUTXO UTXOSet) (acceptedSelectedParentUTXO UTXOSet, txAcceptanceData []TxAcceptanceData, err error) {
|
func (node *blockNode) acceptSelectedParentTransactions(selectedParent *util.Block, selectedParentUTXO UTXOSet) (acceptedSelectedParentUTXO UTXOSet, txAcceptanceData []TxAcceptanceData, err error) {
|
||||||
@ -1381,11 +1523,6 @@ func (dag *BlockDAG) UTXOConfirmations(outpoint *wire.Outpoint) (uint64, bool) {
|
|||||||
return confirmations, true
|
return confirmations, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTXOCommitment returns a commitment to the dag's current UTXOSet
|
|
||||||
func (dag *BlockDAG) UTXOCommitment() string {
|
|
||||||
return dag.UTXOSet().UTXOMultiset.Hash().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockConfirmations returns the current confirmations number of the given node
|
// blockConfirmations returns the current confirmations number of the given node
|
||||||
// The confirmations number is defined as follows:
|
// The confirmations number is defined as follows:
|
||||||
// * If the node is in the selected tip red set -> 0
|
// * If the node is in the selected tip red set -> 0
|
||||||
@ -1952,6 +2089,7 @@ func New(config *Config) (*BlockDAG, error) {
|
|||||||
dag.virtual = newVirtualBlock(dag, nil)
|
dag.virtual = newVirtualBlock(dag, nil)
|
||||||
dag.utxoDiffStore = newUTXODiffStore(dag)
|
dag.utxoDiffStore = newUTXODiffStore(dag)
|
||||||
dag.reachabilityStore = newReachabilityStore(dag)
|
dag.reachabilityStore = newReachabilityStore(dag)
|
||||||
|
dag.multisetStore = newMultisetStore(dag)
|
||||||
|
|
||||||
// Initialize the DAG state from the passed database. When the db
|
// Initialize the DAG state from the passed database. When the db
|
||||||
// does not yet contain any DAG state, both it and the DAG state
|
// does not yet contain any DAG state, both it and the DAG state
|
||||||
|
@ -59,6 +59,10 @@ var (
|
|||||||
// reachability tree nodes and future covering sets of blocks.
|
// reachability tree nodes and future covering sets of blocks.
|
||||||
reachabilityDataBucketName = []byte("reachability")
|
reachabilityDataBucketName = []byte("reachability")
|
||||||
|
|
||||||
|
// multisetBucketName is the name of the database bucket used to house the
|
||||||
|
// ECMH multisets of blocks.
|
||||||
|
multisetBucketName = []byte("multiset")
|
||||||
|
|
||||||
// subnetworksBucketName is the name of the database bucket used to store the
|
// subnetworksBucketName is the name of the database bucket used to store the
|
||||||
// subnetwork registry.
|
// subnetwork registry.
|
||||||
subnetworksBucketName = []byte("subnetworks")
|
subnetworksBucketName = []byte("subnetworks")
|
||||||
@ -265,6 +269,11 @@ func (dag *BlockDAG) createDAGState() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = meta.CreateBucket(multisetBucketName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = dbPutVersion(dbTx, utxoSetVersionKeyName,
|
err = dbPutVersion(dbTx, utxoSetVersionKeyName,
|
||||||
latestUTXOSetBucketVersion)
|
latestUTXOSetBucketVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -320,6 +329,11 @@ func (dag *BlockDAG) removeDAGState() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = meta.DeleteBucket(multisetBucketName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = dbTx.Metadata().Delete(utxoSetVersionKeyName)
|
err = dbTx.Metadata().Delete(utxoSetVersionKeyName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -485,11 +499,19 @@ func (dag *BlockDAG) initDAGState() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the reachability store
|
// Initialize the reachability store
|
||||||
|
log.Infof("Loading reachability data...")
|
||||||
err = dag.reachabilityStore.init(dbTx)
|
err = dag.reachabilityStore.init(dbTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize the multiset store
|
||||||
|
log.Infof("Loading multiset data...")
|
||||||
|
err = dag.multisetStore.init(dbTx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Apply the loaded utxoCollection to the virtual block.
|
// Apply the loaded utxoCollection to the virtual block.
|
||||||
dag.virtual.utxoSet, err = newFullUTXOSetFromUTXOCollection(fullUTXOCollection)
|
dag.virtual.utxoSet, err = newFullUTXOSetFromUTXOCollection(fullUTXOCollection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -267,11 +267,19 @@ func TestChainedTransactions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
chainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{chainedTxIn}, []*wire.TxOut{chainedTxOut})
|
chainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{chainedTxIn}, []*wire.TxOut{chainedTxOut})
|
||||||
|
|
||||||
block2, err := mining.PrepareBlockForTest(dag, ¶ms, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{tx, chainedTx}, true)
|
block2, err := mining.PrepareBlockForTest(dag, ¶ms, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{tx}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("PrepareBlockForTest: %v", err)
|
t.Fatalf("PrepareBlockForTest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manually add a chained transaction to block2
|
||||||
|
block2.Transactions = append(block2.Transactions, chainedTx)
|
||||||
|
block2UtilTxs := make([]*util.Tx, len(block2.Transactions))
|
||||||
|
for i, tx := range block2.Transactions {
|
||||||
|
block2UtilTxs[i] = util.NewTx(tx)
|
||||||
|
}
|
||||||
|
block2.Header.HashMerkleRoot = blockdag.BuildHashMerkleTreeStore(block2UtilTxs).Root()
|
||||||
|
|
||||||
//Checks that dag.ProcessBlock fails because we don't allow a transaction to spend another transaction from the same block
|
//Checks that dag.ProcessBlock fails because we don't allow a transaction to spend another transaction from the same block
|
||||||
isOrphan, isDelayed, err = dag.ProcessBlock(util.NewBlock(block2), blockdag.BFNoPoWCheck)
|
isOrphan, isDelayed, err = dag.ProcessBlock(util.NewBlock(block2), blockdag.BFNoPoWCheck)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -3,6 +3,7 @@ package blockdag
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"github.com/kaspanet/kaspad/ecc"
|
||||||
"github.com/kaspanet/kaspad/txscript"
|
"github.com/kaspanet/kaspad/txscript"
|
||||||
"github.com/kaspanet/kaspad/util"
|
"github.com/kaspanet/kaspad/util"
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
@ -12,6 +13,8 @@ import (
|
|||||||
// BlockForMining returns a block with the given transactions
|
// BlockForMining returns a block with the given transactions
|
||||||
// that points to the current DAG tips, that is valid from
|
// that points to the current DAG tips, that is valid from
|
||||||
// all aspects except proof of work.
|
// all aspects except proof of work.
|
||||||
|
//
|
||||||
|
// This function MUST be called with the DAG state lock held (for reads).
|
||||||
func (dag *BlockDAG) BlockForMining(transactions []*util.Tx) (*wire.MsgBlock, error) {
|
func (dag *BlockDAG) BlockForMining(transactions []*util.Tx) (*wire.MsgBlock, error) {
|
||||||
blockTimestamp := dag.NextBlockTime()
|
blockTimestamp := dag.NextBlockTime()
|
||||||
requiredDifficulty := dag.NextRequiredDifficulty(blockTimestamp)
|
requiredDifficulty := dag.NextRequiredDifficulty(blockTimestamp)
|
||||||
@ -34,18 +37,17 @@ func (dag *BlockDAG) BlockForMining(transactions []*util.Tx) (*wire.MsgBlock, er
|
|||||||
msgBlock.AddTransaction(tx.MsgTx())
|
msgBlock.AddTransaction(tx.MsgTx())
|
||||||
}
|
}
|
||||||
|
|
||||||
utxoWithTransactions, err := dag.UTXOSet().WithTransactions(msgBlock.Transactions, UnacceptedBlueScore, false)
|
multiset, err := dag.NextBlockMultiset(transactions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
utxoCommitment := utxoWithTransactions.Multiset().Hash()
|
|
||||||
|
|
||||||
msgBlock.Header = wire.BlockHeader{
|
msgBlock.Header = wire.BlockHeader{
|
||||||
Version: nextBlockVersion,
|
Version: nextBlockVersion,
|
||||||
ParentHashes: dag.TipHashes(),
|
ParentHashes: dag.TipHashes(),
|
||||||
HashMerkleRoot: hashMerkleTree.Root(),
|
HashMerkleRoot: hashMerkleTree.Root(),
|
||||||
AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
|
AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
|
||||||
UTXOCommitment: utxoCommitment,
|
UTXOCommitment: multiset.Hash(),
|
||||||
Timestamp: blockTimestamp,
|
Timestamp: blockTimestamp,
|
||||||
Bits: requiredDifficulty,
|
Bits: requiredDifficulty,
|
||||||
}
|
}
|
||||||
@ -53,6 +55,19 @@ func (dag *BlockDAG) BlockForMining(transactions []*util.Tx) (*wire.MsgBlock, er
|
|||||||
return &msgBlock, nil
|
return &msgBlock, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NextBlockMultiset returns the multiset of an assumed next block
|
||||||
|
// built on top of the current tips, with the given transactions.
|
||||||
|
//
|
||||||
|
// This function MUST be called with the DAG state lock held (for reads).
|
||||||
|
func (dag *BlockDAG) NextBlockMultiset(transactions []*util.Tx) (*ecc.Multiset, error) {
|
||||||
|
pastUTXO, selectedParentUTXO, txsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dag.virtual.blockNode.calcMultiset(dag, transactions, txsAcceptanceData, selectedParentUTXO, pastUTXO)
|
||||||
|
}
|
||||||
|
|
||||||
// CoinbasePayloadExtraData returns coinbase payload extra data parameter
|
// CoinbasePayloadExtraData returns coinbase payload extra data parameter
|
||||||
// which is built from extra nonce and coinbase flags.
|
// which is built from extra nonce and coinbase flags.
|
||||||
func CoinbasePayloadExtraData(extraNonce uint64, coinbaseFlags string) ([]byte, error) {
|
func CoinbasePayloadExtraData(extraNonce uint64, coinbaseFlags string) ([]byte, error) {
|
||||||
|
49
blockdag/multisetio.go
Normal file
49
blockdag/multisetio.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package blockdag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"github.com/kaspanet/kaspad/ecc"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
const multisetPointSize = 32
|
||||||
|
|
||||||
|
// serializeMultiset serializes an ECMH multiset. The serialization
|
||||||
|
// uses the following format: <x (32 bytes)><y (32 bytes)>.
|
||||||
|
func serializeMultiset(w io.Writer, ms *ecc.Multiset) error {
|
||||||
|
x, y := ms.Point()
|
||||||
|
xBytes := make([]byte, multisetPointSize)
|
||||||
|
copy(xBytes, x.Bytes())
|
||||||
|
yBytes := make([]byte, multisetPointSize)
|
||||||
|
copy(yBytes, y.Bytes())
|
||||||
|
|
||||||
|
err := binary.Write(w, byteOrder, xBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = binary.Write(w, byteOrder, yBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deserializeMultiset deserializes an EMCH multiset.
|
||||||
|
// See serializeMultiset for more details.
|
||||||
|
func deserializeMultiset(r io.Reader) (*ecc.Multiset, error) {
|
||||||
|
xBytes := make([]byte, multisetPointSize)
|
||||||
|
yBytes := make([]byte, multisetPointSize)
|
||||||
|
err := binary.Read(r, byteOrder, xBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = binary.Read(r, byteOrder, yBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var x, y big.Int
|
||||||
|
x.SetBytes(xBytes)
|
||||||
|
y.SetBytes(yBytes)
|
||||||
|
return ecc.NewMultisetFromPoint(ecc.S256(), &x, &y), nil
|
||||||
|
}
|
112
blockdag/multisetstore.go
Normal file
112
blockdag/multisetstore.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package blockdag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/kaspanet/kaspad/database"
|
||||||
|
"github.com/kaspanet/kaspad/ecc"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/util/locks"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type multisetStore struct {
|
||||||
|
dag *BlockDAG
|
||||||
|
new map[daghash.Hash]struct{}
|
||||||
|
loaded map[daghash.Hash]*ecc.Multiset
|
||||||
|
mtx *locks.PriorityMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMultisetStore(dag *BlockDAG) *multisetStore {
|
||||||
|
return &multisetStore{
|
||||||
|
dag: dag,
|
||||||
|
new: make(map[daghash.Hash]struct{}),
|
||||||
|
loaded: make(map[daghash.Hash]*ecc.Multiset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *multisetStore) setMultiset(node *blockNode, ms *ecc.Multiset) {
|
||||||
|
store.loaded[*node.hash] = ms
|
||||||
|
store.addToNewBlocks(node.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *multisetStore) addToNewBlocks(blockHash *daghash.Hash) {
|
||||||
|
store.new[*blockHash] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func multisetNotFoundError(blockHash *daghash.Hash) error {
|
||||||
|
return errors.Errorf("Couldn't find multiset data for block %s", blockHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *multisetStore) multisetByBlockNode(node *blockNode) (*ecc.Multiset, error) {
|
||||||
|
ms, exists := store.multisetByBlockHash(node.hash)
|
||||||
|
if !exists {
|
||||||
|
return nil, multisetNotFoundError(node.hash)
|
||||||
|
}
|
||||||
|
return ms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *multisetStore) multisetByBlockHash(hash *daghash.Hash) (*ecc.Multiset, bool) {
|
||||||
|
ms, ok := store.loaded[*hash]
|
||||||
|
return ms, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// flushToDB writes all new multiset data to the database.
|
||||||
|
func (store *multisetStore) flushToDB(dbTx database.Tx) error {
|
||||||
|
if len(store.new) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &bytes.Buffer{}
|
||||||
|
for hash := range store.new {
|
||||||
|
hash := hash // Copy hash to a new variable to avoid passing the same pointer
|
||||||
|
|
||||||
|
w.Reset()
|
||||||
|
ms, exists := store.loaded[hash]
|
||||||
|
if !exists {
|
||||||
|
return multisetNotFoundError(&hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := serializeMultiset(w, ms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = store.dbStoreMultiset(dbTx, &hash, w.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *multisetStore) clearNewEntries() {
|
||||||
|
store.new = make(map[daghash.Hash]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *multisetStore) init(dbTx database.Tx) error {
|
||||||
|
bucket := dbTx.Metadata().Bucket(multisetBucketName)
|
||||||
|
cursor := bucket.Cursor()
|
||||||
|
for ok := cursor.First(); ok; ok = cursor.Next() {
|
||||||
|
hash, err := daghash.NewHash(cursor.Key())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ms, err := deserializeMultiset(bytes.NewReader(cursor.Value()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
store.loaded[*hash] = ms
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbStoreMultiset stores the multiset data to the database.
|
||||||
|
func (store *multisetStore) dbStoreMultiset(dbTx database.Tx, blockHash *daghash.Hash, serializedMS []byte) error {
|
||||||
|
bucket := dbTx.Metadata().Bucket(multisetBucketName)
|
||||||
|
if bucket.Get(blockHash[:]) != nil {
|
||||||
|
return errors.Errorf("Can't override an existing multiset database entry for block %s", blockHash)
|
||||||
|
}
|
||||||
|
return bucket.Put(blockHash[:], serializedMS)
|
||||||
|
}
|
@ -173,7 +173,7 @@ func GetVirtualFromParentsForTest(dag *BlockDAG, parentHashes []*daghash.Hash) (
|
|||||||
}
|
}
|
||||||
virtual := newVirtualBlock(dag, parents)
|
virtual := newVirtualBlock(dag, parents)
|
||||||
|
|
||||||
pastUTXO, _, err := dag.pastUTXO(&virtual.blockNode)
|
pastUTXO, _, _, err := dag.pastUTXO(&virtual.blockNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2,31 +2,24 @@ package blockdag
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/golang/groupcache/lru"
|
|
||||||
"github.com/kaspanet/kaspad/ecc"
|
"github.com/kaspanet/kaspad/ecc"
|
||||||
"github.com/kaspanet/kaspad/util/daghash"
|
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ecmhCacheSize = 4_000_000
|
func addUTXOToMultiset(ms *ecc.Multiset, entry *UTXOEntry, outpoint *wire.Outpoint) (*ecc.Multiset, error) {
|
||||||
|
|
||||||
var (
|
|
||||||
utxoToECMHCache = lru.New(ecmhCacheSize)
|
|
||||||
)
|
|
||||||
|
|
||||||
func utxoMultiset(entry *UTXOEntry, outpoint *wire.Outpoint) (*ecc.Multiset, error) {
|
|
||||||
w := &bytes.Buffer{}
|
w := &bytes.Buffer{}
|
||||||
err := serializeUTXO(w, entry, outpoint)
|
err := serializeUTXO(w, entry, outpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
serializedUTXO := w.Bytes()
|
return ms.Add(w.Bytes()), nil
|
||||||
utxoHash := daghash.DoubleHashH(serializedUTXO)
|
}
|
||||||
|
|
||||||
if cachedMSPoint, ok := utxoToECMHCache.Get(utxoHash); ok {
|
func removeUTXOFromMultiset(ms *ecc.Multiset, entry *UTXOEntry, outpoint *wire.Outpoint) (*ecc.Multiset, error) {
|
||||||
return cachedMSPoint.(*ecc.Multiset), nil
|
w := &bytes.Buffer{}
|
||||||
|
err := serializeUTXO(w, entry, outpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
msPoint := ecc.NewMultiset(ecc.S256()).Add(serializedUTXO)
|
return ms.Remove(w.Bytes()), nil
|
||||||
utxoToECMHCache.Add(utxoHash, msPoint)
|
|
||||||
return msPoint, nil
|
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,6 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var multisetPointSize = 32
|
|
||||||
|
|
||||||
type blockUTXODiffData struct {
|
type blockUTXODiffData struct {
|
||||||
diff *UTXODiff
|
diff *UTXODiff
|
||||||
diffChild *blockNode
|
diffChild *blockNode
|
||||||
|
@ -2,15 +2,11 @@ package blockdag
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"github.com/kaspanet/kaspad/util/binaryserializer"
|
"github.com/kaspanet/kaspad/util/binaryserializer"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/ecc"
|
|
||||||
"github.com/kaspanet/kaspad/util/daghash"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// serializeBlockUTXODiffData serializes diff data in the following format:
|
// serializeBlockUTXODiffData serializes diff data in the following format:
|
||||||
@ -82,9 +78,7 @@ func (diffStore *utxoDiffStore) deserializeBlockUTXODiffData(serializedDiffData
|
|||||||
}
|
}
|
||||||
|
|
||||||
func deserializeUTXODiff(r io.Reader) (*UTXODiff, error) {
|
func deserializeUTXODiff(r io.Reader) (*UTXODiff, error) {
|
||||||
diff := &UTXODiff{
|
diff := &UTXODiff{}
|
||||||
useMultiset: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
diff.toAdd, err = deserializeUTXOCollection(r)
|
diff.toAdd, err = deserializeUTXOCollection(r)
|
||||||
@ -97,11 +91,6 @@ func deserializeUTXODiff(r io.Reader) (*UTXODiff, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
diff.diffMultiset, err = deserializeMultiset(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return diff, nil
|
return diff, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,31 +123,9 @@ func deserializeUTXO(r io.Reader) (*UTXOEntry, *wire.Outpoint, error) {
|
|||||||
return utxoEntry, outpoint, nil
|
return utxoEntry, outpoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deserializeMultiset deserializes an EMCH multiset.
|
|
||||||
// See serializeMultiset for more details.
|
|
||||||
func deserializeMultiset(r io.Reader) (*ecc.Multiset, error) {
|
|
||||||
xBytes := make([]byte, multisetPointSize)
|
|
||||||
yBytes := make([]byte, multisetPointSize)
|
|
||||||
err := binary.Read(r, byteOrder, xBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Read(r, byteOrder, yBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var x, y big.Int
|
|
||||||
x.SetBytes(xBytes)
|
|
||||||
y.SetBytes(yBytes)
|
|
||||||
return ecc.NewMultisetFromPoint(ecc.S256(), &x, &y), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// serializeUTXODiff serializes UTXODiff by serializing
|
// serializeUTXODiff serializes UTXODiff by serializing
|
||||||
// UTXODiff.toAdd, UTXODiff.toRemove and UTXODiff.Multiset one after the other.
|
// UTXODiff.toAdd, UTXODiff.toRemove and UTXODiff.Multiset one after the other.
|
||||||
func serializeUTXODiff(w io.Writer, diff *UTXODiff) error {
|
func serializeUTXODiff(w io.Writer, diff *UTXODiff) error {
|
||||||
if !diff.useMultiset {
|
|
||||||
return errors.New("Cannot serialize a UTXO diff without a multiset")
|
|
||||||
}
|
|
||||||
err := serializeUTXOCollection(w, diff.toAdd)
|
err := serializeUTXOCollection(w, diff.toAdd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -168,10 +135,7 @@ func serializeUTXODiff(w io.Writer, diff *UTXODiff) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = serializeMultiset(w, diff.diffMultiset)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,29 +156,6 @@ func serializeUTXOCollection(w io.Writer, collection utxoCollection) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// serializeMultiset serializes an ECMH multiset. The serialization
|
|
||||||
// is done by taking the (x,y) coordinnates of the multiset point and
|
|
||||||
// padding each one of them with 32 byte (it'll be 32 byte in most
|
|
||||||
// cases anyway except one of the coordinates is zero) and writing
|
|
||||||
// them one after the other.
|
|
||||||
func serializeMultiset(w io.Writer, ms *ecc.Multiset) error {
|
|
||||||
x, y := ms.Point()
|
|
||||||
xBytes := make([]byte, multisetPointSize)
|
|
||||||
copy(xBytes, x.Bytes())
|
|
||||||
yBytes := make([]byte, multisetPointSize)
|
|
||||||
copy(yBytes, y.Bytes())
|
|
||||||
|
|
||||||
err := binary.Write(w, byteOrder, xBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = binary.Write(w, byteOrder, yBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// serializeUTXO serializes a utxo entry-outpoint pair
|
// serializeUTXO serializes a utxo entry-outpoint pair
|
||||||
func serializeUTXO(w io.Writer, entry *UTXOEntry, outpoint *wire.Outpoint) error {
|
func serializeUTXO(w io.Writer, entry *UTXOEntry, outpoint *wire.Outpoint) error {
|
||||||
err := serializeOutpoint(w, outpoint)
|
err := serializeOutpoint(w, outpoint)
|
||||||
|
@ -155,27 +155,14 @@ func (uc utxoCollection) clone() utxoCollection {
|
|||||||
type UTXODiff struct {
|
type UTXODiff struct {
|
||||||
toAdd utxoCollection
|
toAdd utxoCollection
|
||||||
toRemove utxoCollection
|
toRemove utxoCollection
|
||||||
diffMultiset *ecc.Multiset
|
|
||||||
useMultiset bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUTXODiffWithoutMultiset creates a new, empty utxoDiff
|
// NewUTXODiff creates a new, empty utxoDiff
|
||||||
// without a multiset.
|
// without a multiset.
|
||||||
func NewUTXODiffWithoutMultiset() *UTXODiff {
|
|
||||||
return &UTXODiff{
|
|
||||||
toAdd: utxoCollection{},
|
|
||||||
toRemove: utxoCollection{},
|
|
||||||
useMultiset: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUTXODiff creates a new, empty utxoDiff.
|
|
||||||
func NewUTXODiff() *UTXODiff {
|
func NewUTXODiff() *UTXODiff {
|
||||||
return &UTXODiff{
|
return &UTXODiff{
|
||||||
toAdd: utxoCollection{},
|
toAdd: utxoCollection{},
|
||||||
toRemove: utxoCollection{},
|
toRemove: utxoCollection{},
|
||||||
useMultiset: true,
|
|
||||||
diffMultiset: ecc.NewMultiset(ecc.S256()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +198,6 @@ func (d *UTXODiff) diffFrom(other *UTXODiff) (*UTXODiff, error) {
|
|||||||
result := UTXODiff{
|
result := UTXODiff{
|
||||||
toAdd: make(utxoCollection, len(d.toRemove)+len(other.toAdd)),
|
toAdd: make(utxoCollection, len(d.toRemove)+len(other.toAdd)),
|
||||||
toRemove: make(utxoCollection, len(d.toAdd)+len(other.toRemove)),
|
toRemove: make(utxoCollection, len(d.toAdd)+len(other.toRemove)),
|
||||||
useMultiset: d.useMultiset,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that the following cases are not accounted for, as they are impossible
|
// Note that the following cases are not accounted for, as they are impossible
|
||||||
@ -293,11 +279,6 @@ func (d *UTXODiff) diffFrom(other *UTXODiff) (*UTXODiff, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.useMultiset {
|
|
||||||
// Create a new diffMultiset as the subtraction of the two diffs.
|
|
||||||
result.diffMultiset = other.diffMultiset.Subtract(d.diffMultiset)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,11 +324,6 @@ func (d *UTXODiff) withDiffInPlace(diff *UTXODiff) error {
|
|||||||
d.toAdd.add(outpoint, entryToAdd)
|
d.toAdd.add(outpoint, entryToAdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply diff.diffMultiset to d.diffMultiset
|
|
||||||
if d.useMultiset {
|
|
||||||
d.diffMultiset = d.diffMultiset.Union(diff.diffMultiset)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,10 +345,6 @@ func (d *UTXODiff) clone() *UTXODiff {
|
|||||||
clone := &UTXODiff{
|
clone := &UTXODiff{
|
||||||
toAdd: d.toAdd.clone(),
|
toAdd: d.toAdd.clone(),
|
||||||
toRemove: d.toRemove.clone(),
|
toRemove: d.toRemove.clone(),
|
||||||
useMultiset: d.useMultiset,
|
|
||||||
}
|
|
||||||
if d.useMultiset {
|
|
||||||
clone.diffMultiset = d.diffMultiset.Clone()
|
|
||||||
}
|
}
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
@ -389,14 +361,6 @@ func (d *UTXODiff) AddEntry(outpoint wire.Outpoint, entry *UTXOEntry) error {
|
|||||||
} else {
|
} else {
|
||||||
d.toAdd.add(outpoint, entry)
|
d.toAdd.add(outpoint, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.useMultiset {
|
|
||||||
newMs, err := addUTXOToMultiset(d.diffMultiset, entry, &outpoint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.diffMultiset = newMs
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,21 +376,10 @@ func (d *UTXODiff) RemoveEntry(outpoint wire.Outpoint, entry *UTXOEntry) error {
|
|||||||
} else {
|
} else {
|
||||||
d.toRemove.add(outpoint, entry)
|
d.toRemove.add(outpoint, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.useMultiset {
|
|
||||||
newMs, err := removeUTXOFromMultiset(d.diffMultiset, entry, &outpoint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.diffMultiset = newMs
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d UTXODiff) String() string {
|
func (d UTXODiff) String() string {
|
||||||
if d.useMultiset {
|
|
||||||
return fmt.Sprintf("toAdd: %s; toRemove: %s, Multiset-Hash: %s", d.toAdd, d.toRemove, d.diffMultiset.Hash())
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("toAdd: %s; toRemove: %s", d.toAdd, d.toRemove)
|
return fmt.Sprintf("toAdd: %s; toRemove: %s", d.toAdd, d.toRemove)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,8 +404,6 @@ type UTXOSet interface {
|
|||||||
AddTx(tx *wire.MsgTx, blockBlueScore uint64) (ok bool, err error)
|
AddTx(tx *wire.MsgTx, blockBlueScore uint64) (ok bool, err error)
|
||||||
clone() UTXOSet
|
clone() UTXOSet
|
||||||
Get(outpoint wire.Outpoint) (*UTXOEntry, bool)
|
Get(outpoint wire.Outpoint) (*UTXOEntry, bool)
|
||||||
Multiset() *ecc.Multiset
|
|
||||||
WithTransactions(transactions []*wire.MsgTx, blockBlueScore uint64, ignoreDoubleSpends bool) (UTXOSet, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// diffFromTx is a common implementation for diffFromTx, that works
|
// diffFromTx is a common implementation for diffFromTx, that works
|
||||||
@ -522,14 +473,12 @@ func diffFromAcceptedTx(u UTXOSet, tx *wire.MsgTx, acceptingBlueScore uint64) (*
|
|||||||
// FullUTXOSet represents a full list of transaction outputs and their values
|
// FullUTXOSet represents a full list of transaction outputs and their values
|
||||||
type FullUTXOSet struct {
|
type FullUTXOSet struct {
|
||||||
utxoCollection
|
utxoCollection
|
||||||
UTXOMultiset *ecc.Multiset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFullUTXOSet creates a new utxoSet with full list of transaction outputs and their values
|
// NewFullUTXOSet creates a new utxoSet with full list of transaction outputs and their values
|
||||||
func NewFullUTXOSet() *FullUTXOSet {
|
func NewFullUTXOSet() *FullUTXOSet {
|
||||||
return &FullUTXOSet{
|
return &FullUTXOSet{
|
||||||
utxoCollection: utxoCollection{},
|
utxoCollection: utxoCollection{},
|
||||||
UTXOMultiset: ecc.NewMultiset(ecc.S256()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +494,6 @@ func newFullUTXOSetFromUTXOCollection(collection utxoCollection) (*FullUTXOSet,
|
|||||||
}
|
}
|
||||||
return &FullUTXOSet{
|
return &FullUTXOSet{
|
||||||
utxoCollection: collection,
|
utxoCollection: collection,
|
||||||
UTXOMultiset: multiset,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,22 +530,14 @@ func (fus *FullUTXOSet) AddTx(tx *wire.MsgTx, blueScore uint64) (isAccepted bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range tx.TxIn {
|
||||||
outpoint := *wire.NewOutpoint(&txIn.PreviousOutpoint.TxID, txIn.PreviousOutpoint.Index)
|
fus.remove(txIn.PreviousOutpoint)
|
||||||
err := fus.removeAndUpdateMultiset(outpoint)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, txOut := range tx.TxOut {
|
for i, txOut := range tx.TxOut {
|
||||||
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
|
outpoint := *wire.NewOutpoint(tx.TxID(), uint32(i))
|
||||||
entry := NewUTXOEntry(txOut, isCoinbase, blueScore)
|
entry := NewUTXOEntry(txOut, isCoinbase, blueScore)
|
||||||
|
fus.add(outpoint, entry)
|
||||||
err := fus.addAndUpdateMultiset(outpoint, entry)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -626,7 +566,7 @@ func (fus *FullUTXOSet) diffFromAcceptedTx(tx *wire.MsgTx, acceptingBlueScore ui
|
|||||||
|
|
||||||
// clone returns a clone of this utxoSet
|
// clone returns a clone of this utxoSet
|
||||||
func (fus *FullUTXOSet) clone() UTXOSet {
|
func (fus *FullUTXOSet) clone() UTXOSet {
|
||||||
return &FullUTXOSet{utxoCollection: fus.utxoCollection.clone(), UTXOMultiset: fus.UTXOMultiset.Clone()}
|
return &FullUTXOSet{utxoCollection: fus.utxoCollection.clone()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the UTXOEntry associated with the given Outpoint, and a boolean indicating if such entry was found
|
// Get returns the UTXOEntry associated with the given Outpoint, and a boolean indicating if such entry was found
|
||||||
@ -635,55 +575,6 @@ func (fus *FullUTXOSet) Get(outpoint wire.Outpoint) (*UTXOEntry, bool) {
|
|||||||
return utxoEntry, ok
|
return utxoEntry, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiset returns the ecmh-Multiset of this utxoSet
|
|
||||||
func (fus *FullUTXOSet) Multiset() *ecc.Multiset {
|
|
||||||
return fus.UTXOMultiset
|
|
||||||
}
|
|
||||||
|
|
||||||
// addAndUpdateMultiset adds a UTXOEntry to this utxoSet and updates its multiset accordingly
|
|
||||||
func (fus *FullUTXOSet) addAndUpdateMultiset(outpoint wire.Outpoint, entry *UTXOEntry) error {
|
|
||||||
fus.add(outpoint, entry)
|
|
||||||
newMs, err := addUTXOToMultiset(fus.UTXOMultiset, entry, &outpoint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fus.UTXOMultiset = newMs
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeAndUpdateMultiset removes a UTXOEntry from this utxoSet and updates its multiset accordingly
|
|
||||||
func (fus *FullUTXOSet) removeAndUpdateMultiset(outpoint wire.Outpoint) error {
|
|
||||||
entry, ok := fus.Get(outpoint)
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("Couldn't find outpoint %s", outpoint)
|
|
||||||
}
|
|
||||||
fus.remove(outpoint)
|
|
||||||
var err error
|
|
||||||
newMs, err := removeUTXOFromMultiset(fus.UTXOMultiset, entry, &outpoint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fus.UTXOMultiset = newMs
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTransactions returns a new UTXO Set with the added transactions.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the DAG lock held.
|
|
||||||
func (fus *FullUTXOSet) WithTransactions(transactions []*wire.MsgTx, blockBlueScore uint64, ignoreDoubleSpends bool) (UTXOSet, error) {
|
|
||||||
diffSet := NewDiffUTXOSet(fus, NewUTXODiff())
|
|
||||||
for _, tx := range transactions {
|
|
||||||
isAccepted, err := diffSet.AddTx(tx, blockBlueScore)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !ignoreDoubleSpends && !isAccepted {
|
|
||||||
return nil, errors.Errorf("Transaction %s is not valid with the current UTXO set", tx.TxID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return UTXOSet(diffSet), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiffUTXOSet represents a utxoSet with a base fullUTXOSet and a UTXODiff
|
// DiffUTXOSet represents a utxoSet with a base fullUTXOSet and a UTXODiff
|
||||||
type DiffUTXOSet struct {
|
type DiffUTXOSet struct {
|
||||||
base *FullUTXOSet
|
base *FullUTXOSet
|
||||||
@ -744,12 +635,11 @@ func (dus *DiffUTXOSet) AddTx(tx *wire.MsgTx, blockBlueScore uint64) (bool, erro
|
|||||||
func (dus *DiffUTXOSet) appendTx(tx *wire.MsgTx, blockBlueScore uint64, isCoinbase bool) error {
|
func (dus *DiffUTXOSet) appendTx(tx *wire.MsgTx, blockBlueScore uint64, isCoinbase bool) error {
|
||||||
if !isCoinbase {
|
if !isCoinbase {
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range tx.TxIn {
|
||||||
outpoint := *wire.NewOutpoint(&txIn.PreviousOutpoint.TxID, txIn.PreviousOutpoint.Index)
|
entry, ok := dus.Get(txIn.PreviousOutpoint)
|
||||||
entry, ok := dus.Get(outpoint)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.Errorf("Couldn't find entry for outpoint %s", outpoint)
|
return errors.Errorf("Couldn't find entry for outpoint %s", txIn.PreviousOutpoint)
|
||||||
}
|
}
|
||||||
err := dus.UTXODiff.RemoveEntry(outpoint, entry)
|
err := dus.UTXODiff.RemoveEntry(txIn.PreviousOutpoint, entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -795,16 +685,7 @@ func (dus *DiffUTXOSet) meldToBase() error {
|
|||||||
for outpoint, utxoEntry := range dus.UTXODiff.toAdd {
|
for outpoint, utxoEntry := range dus.UTXODiff.toAdd {
|
||||||
dus.base.add(outpoint, utxoEntry)
|
dus.base.add(outpoint, utxoEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dus.UTXODiff.useMultiset {
|
|
||||||
dus.base.UTXOMultiset = dus.base.UTXOMultiset.Union(dus.UTXODiff.diffMultiset)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dus.UTXODiff.useMultiset {
|
|
||||||
dus.UTXODiff = NewUTXODiff()
|
dus.UTXODiff = NewUTXODiff()
|
||||||
} else {
|
|
||||||
dus.UTXODiff = NewUTXODiffWithoutMultiset()
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -819,7 +700,7 @@ func (dus *DiffUTXOSet) diffFromAcceptedTx(tx *wire.MsgTx, acceptingBlueScore ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dus *DiffUTXOSet) String() string {
|
func (dus *DiffUTXOSet) String() string {
|
||||||
return fmt.Sprintf("{Base: %s, To Add: %s, To Remove: %s, Multiset-Hash:%s}", dus.base, dus.UTXODiff.toAdd, dus.UTXODiff.toRemove, dus.Multiset().Hash())
|
return fmt.Sprintf("{Base: %s, To Add: %s, To Remove: %s}", dus.base, dus.UTXODiff.toAdd, dus.UTXODiff.toRemove)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone returns a clone of this UTXO Set
|
// clone returns a clone of this UTXO Set
|
||||||
@ -844,42 +725,3 @@ func (dus *DiffUTXOSet) Get(outpoint wire.Outpoint) (*UTXOEntry, bool) {
|
|||||||
txOut, ok := dus.UTXODiff.toAdd.get(outpoint)
|
txOut, ok := dus.UTXODiff.toAdd.get(outpoint)
|
||||||
return txOut, ok
|
return txOut, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiset returns the ecmh-Multiset of this utxoSet
|
|
||||||
func (dus *DiffUTXOSet) Multiset() *ecc.Multiset {
|
|
||||||
return dus.base.UTXOMultiset.Union(dus.UTXODiff.diffMultiset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTransactions returns a new UTXO Set with the added transactions.
|
|
||||||
//
|
|
||||||
// If dus.UTXODiff.useMultiset is true, this function MUST be
|
|
||||||
// called with the DAG lock held.
|
|
||||||
func (dus *DiffUTXOSet) WithTransactions(transactions []*wire.MsgTx, blockBlueScore uint64, ignoreDoubleSpends bool) (UTXOSet, error) {
|
|
||||||
diffSet := NewDiffUTXOSet(dus.base, dus.UTXODiff.clone())
|
|
||||||
for _, tx := range transactions {
|
|
||||||
isAccepted, err := diffSet.AddTx(tx, blockBlueScore)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !ignoreDoubleSpends && !isAccepted {
|
|
||||||
return nil, errors.Errorf("Transaction %s is not valid with the current UTXO set", tx.TxID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return UTXOSet(diffSet), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addUTXOToMultiset(ms *ecc.Multiset, entry *UTXOEntry, outpoint *wire.Outpoint) (*ecc.Multiset, error) {
|
|
||||||
utxoMS, err := utxoMultiset(entry, outpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ms.Union(utxoMS), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeUTXOFromMultiset(ms *ecc.Multiset, entry *UTXOEntry, outpoint *wire.Outpoint) (*ecc.Multiset, error) {
|
|
||||||
utxoMS, err := utxoMultiset(entry, outpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ms.Subtract(utxoMS), nil
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/kaspanet/kaspad/util/subnetworkid"
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/ecc"
|
|
||||||
"github.com/kaspanet/kaspad/util/daghash"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
)
|
)
|
||||||
@ -80,15 +79,10 @@ func TestUTXODiff(t *testing.T) {
|
|||||||
utxoEntry0 := NewUTXOEntry(&wire.TxOut{ScriptPubKey: []byte{}, Value: 10}, true, 0)
|
utxoEntry0 := NewUTXOEntry(&wire.TxOut{ScriptPubKey: []byte{}, Value: 10}, true, 0)
|
||||||
utxoEntry1 := NewUTXOEntry(&wire.TxOut{ScriptPubKey: []byte{}, Value: 20}, false, 1)
|
utxoEntry1 := NewUTXOEntry(&wire.TxOut{ScriptPubKey: []byte{}, Value: 20}, false, 1)
|
||||||
|
|
||||||
for i := 0; i < 2; i++ {
|
|
||||||
withMultiset := i == 0
|
|
||||||
// Test utxoDiff creation
|
// Test utxoDiff creation
|
||||||
var diff *UTXODiff
|
|
||||||
if withMultiset {
|
diff := NewUTXODiff()
|
||||||
diff = NewUTXODiff()
|
|
||||||
} else {
|
|
||||||
diff = NewUTXODiffWithoutMultiset()
|
|
||||||
}
|
|
||||||
if len(diff.toAdd) != 0 || len(diff.toRemove) != 0 {
|
if len(diff.toAdd) != 0 || len(diff.toRemove) != 0 {
|
||||||
t.Errorf("new diff is not empty")
|
t.Errorf("new diff is not empty")
|
||||||
}
|
}
|
||||||
@ -115,16 +109,12 @@ func TestUTXODiff(t *testing.T) {
|
|||||||
|
|
||||||
// Test utxoDiff string representation
|
// Test utxoDiff string representation
|
||||||
expectedDiffString := "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]"
|
expectedDiffString := "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]"
|
||||||
if withMultiset {
|
|
||||||
expectedDiffString = "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], Multiset-Hash: 75cb5bbca4e52a9e478dd3c5af3c856ed0f61b848088014c8c52e70432233a57"
|
|
||||||
}
|
|
||||||
diffString := clonedDiff.String()
|
diffString := clonedDiff.String()
|
||||||
if diffString != expectedDiffString {
|
if diffString != expectedDiffString {
|
||||||
t.Errorf("unexpected diff string. "+
|
t.Errorf("unexpected diff string. "+
|
||||||
"Expected: \"%s\", got: \"%s\".", expectedDiffString, diffString)
|
"Expected: \"%s\", got: \"%s\".", expectedDiffString, diffString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TestUTXODiffRules makes sure that all diffFrom and WithDiff rules are followed.
|
// TestUTXODiffRules makes sure that all diffFrom and WithDiff rules are followed.
|
||||||
// Each test case represents a cell in the two tables outlined in the documentation for utxoDiff.
|
// Each test case represents a cell in the two tables outlined in the documentation for utxoDiff.
|
||||||
@ -542,157 +532,101 @@ func TestUTXODiffRules(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
this := addMultisetToDiff(t, test.this)
|
// diffFrom from test.this to test.other
|
||||||
other := addMultisetToDiff(t, test.other)
|
diffResult, err := test.this.diffFrom(test.other)
|
||||||
expectedDiffFromResult := addMultisetToDiff(t, test.expectedDiffFromResult)
|
|
||||||
expectedWithDiffResult := addMultisetToDiff(t, test.expectedWithDiffResult)
|
|
||||||
|
|
||||||
// diffFrom from this to other
|
|
||||||
diffResult, err := this.diffFrom(other)
|
|
||||||
|
|
||||||
// Test whether diffFrom returned an error
|
// Test whether diffFrom returned an error
|
||||||
isDiffFromOk := err == nil
|
isDiffFromOk := err == nil
|
||||||
expectedIsDiffFromOk := expectedDiffFromResult != nil
|
expectedIsDiffFromOk := test.expectedDiffFromResult != nil
|
||||||
if isDiffFromOk != expectedIsDiffFromOk {
|
if isDiffFromOk != expectedIsDiffFromOk {
|
||||||
t.Errorf("unexpected diffFrom error in test \"%s\". "+
|
t.Errorf("unexpected diffFrom error in test \"%s\". "+
|
||||||
"Expected: \"%t\", got: \"%t\".", test.name, expectedIsDiffFromOk, isDiffFromOk)
|
"Expected: \"%t\", got: \"%t\".", test.name, expectedIsDiffFromOk, isDiffFromOk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not error, test the diffFrom result
|
// If not error, test the diffFrom result
|
||||||
if isDiffFromOk && !expectedDiffFromResult.equal(diffResult) {
|
if isDiffFromOk && !test.expectedDiffFromResult.equal(diffResult) {
|
||||||
t.Errorf("unexpected diffFrom result in test \"%s\". "+
|
t.Errorf("unexpected diffFrom result in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, expectedDiffFromResult, diffResult)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.expectedDiffFromResult, diffResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that WithDiff after diffFrom results in the original other
|
// Make sure that WithDiff after diffFrom results in the original test.other
|
||||||
if isDiffFromOk {
|
if isDiffFromOk {
|
||||||
otherResult, err := this.WithDiff(diffResult)
|
otherResult, err := test.this.WithDiff(diffResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("WithDiff unexpectedly failed in test \"%s\": %s", test.name, err)
|
t.Errorf("WithDiff unexpectedly failed in test \"%s\": %s", test.name, err)
|
||||||
}
|
}
|
||||||
if !other.equal(otherResult) {
|
if !test.other.equal(otherResult) {
|
||||||
t.Errorf("unexpected WithDiff result in test \"%s\". "+
|
t.Errorf("unexpected WithDiff result in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, other, otherResult)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.other, otherResult)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDiff from this to other
|
// WithDiff from test.this to test.other
|
||||||
withDiffResult, err := this.WithDiff(other)
|
withDiffResult, err := test.this.WithDiff(test.other)
|
||||||
|
|
||||||
// Test whether WithDiff returned an error
|
// Test whether WithDiff returned an error
|
||||||
isWithDiffOk := err == nil
|
isWithDiffOk := err == nil
|
||||||
expectedIsWithDiffOk := expectedWithDiffResult != nil
|
expectedIsWithDiffOk := test.expectedWithDiffResult != nil
|
||||||
if isWithDiffOk != expectedIsWithDiffOk {
|
if isWithDiffOk != expectedIsWithDiffOk {
|
||||||
t.Errorf("unexpected WithDiff error in test \"%s\". "+
|
t.Errorf("unexpected WithDiff error in test \"%s\". "+
|
||||||
"Expected: \"%t\", got: \"%t\".", test.name, expectedIsWithDiffOk, isWithDiffOk)
|
"Expected: \"%t\", got: \"%t\".", test.name, expectedIsWithDiffOk, isWithDiffOk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not error, test the WithDiff result
|
// If not error, test the WithDiff result
|
||||||
if isWithDiffOk && !withDiffResult.equal(expectedWithDiffResult) {
|
if isWithDiffOk && !withDiffResult.equal(test.expectedWithDiffResult) {
|
||||||
t.Errorf("unexpected WithDiff result in test \"%s\". "+
|
t.Errorf("unexpected WithDiff result in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, expectedWithDiffResult, withDiffResult)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.expectedWithDiffResult, withDiffResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeat WithDiff check this time using withDiffInPlace
|
// Repeat WithDiff check test.this time using withDiffInPlace
|
||||||
thisClone := this.clone()
|
thisClone := test.this.clone()
|
||||||
err = thisClone.withDiffInPlace(other)
|
err = thisClone.withDiffInPlace(test.other)
|
||||||
|
|
||||||
// Test whether withDiffInPlace returned an error
|
// Test whether withDiffInPlace returned an error
|
||||||
isWithDiffInPlaceOk := err == nil
|
isWithDiffInPlaceOk := err == nil
|
||||||
expectedIsWithDiffInPlaceOk := expectedWithDiffResult != nil
|
expectedIsWithDiffInPlaceOk := test.expectedWithDiffResult != nil
|
||||||
if isWithDiffInPlaceOk != expectedIsWithDiffInPlaceOk {
|
if isWithDiffInPlaceOk != expectedIsWithDiffInPlaceOk {
|
||||||
t.Errorf("unexpected withDiffInPlace error in test \"%s\". "+
|
t.Errorf("unexpected withDiffInPlace error in test \"%s\". "+
|
||||||
"Expected: \"%t\", got: \"%t\".", test.name, expectedIsWithDiffInPlaceOk, isWithDiffInPlaceOk)
|
"Expected: \"%t\", got: \"%t\".", test.name, expectedIsWithDiffInPlaceOk, isWithDiffInPlaceOk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not error, test the withDiffInPlace result
|
// If not error, test the withDiffInPlace result
|
||||||
if isWithDiffInPlaceOk && !thisClone.equal(expectedWithDiffResult) {
|
if isWithDiffInPlaceOk && !thisClone.equal(test.expectedWithDiffResult) {
|
||||||
t.Errorf("unexpected withDiffInPlace result in test \"%s\". "+
|
t.Errorf("unexpected withDiffInPlace result in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, expectedWithDiffResult, thisClone)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.expectedWithDiffResult, thisClone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that diffFrom after WithDiff results in the original other
|
// Make sure that diffFrom after WithDiff results in the original test.other
|
||||||
if isWithDiffOk {
|
if isWithDiffOk {
|
||||||
otherResult, err := this.diffFrom(withDiffResult)
|
otherResult, err := test.this.diffFrom(withDiffResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("diffFrom unexpectedly failed in test \"%s\": %s", test.name, err)
|
t.Errorf("diffFrom unexpectedly failed in test \"%s\": %s", test.name, err)
|
||||||
}
|
}
|
||||||
if !other.equal(otherResult) {
|
if !test.other.equal(otherResult) {
|
||||||
t.Errorf("unexpected diffFrom result in test \"%s\". "+
|
t.Errorf("unexpected diffFrom result in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, other, otherResult)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.other, otherResult)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func areMultisetsEqual(a *ecc.Multiset, b *ecc.Multiset) bool {
|
|
||||||
aX, aY := a.Point()
|
|
||||||
bX, bY := b.Point()
|
|
||||||
return aX.Cmp(bX) == 0 && aY.Cmp(bY) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *UTXODiff) equal(other *UTXODiff) bool {
|
func (d *UTXODiff) equal(other *UTXODiff) bool {
|
||||||
if d == nil || other == nil {
|
if d == nil || other == nil {
|
||||||
return d == other
|
return d == other
|
||||||
}
|
}
|
||||||
|
|
||||||
return reflect.DeepEqual(d.toAdd, other.toAdd) &&
|
return reflect.DeepEqual(d.toAdd, other.toAdd) &&
|
||||||
reflect.DeepEqual(d.toRemove, other.toRemove) &&
|
reflect.DeepEqual(d.toRemove, other.toRemove)
|
||||||
areMultisetsEqual(d.diffMultiset, other.diffMultiset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fus *FullUTXOSet) equal(other *FullUTXOSet) bool {
|
func (fus *FullUTXOSet) equal(other *FullUTXOSet) bool {
|
||||||
return reflect.DeepEqual(fus.utxoCollection, other.utxoCollection) &&
|
return reflect.DeepEqual(fus.utxoCollection, other.utxoCollection)
|
||||||
areMultisetsEqual(fus.UTXOMultiset, other.UTXOMultiset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dus *DiffUTXOSet) equal(other *DiffUTXOSet) bool {
|
func (dus *DiffUTXOSet) equal(other *DiffUTXOSet) bool {
|
||||||
return dus.base.equal(other.base) && dus.UTXODiff.equal(other.UTXODiff)
|
return dus.base.equal(other.base) && dus.UTXODiff.equal(other.UTXODiff)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMultisetToDiff(t *testing.T, diff *UTXODiff) *UTXODiff {
|
|
||||||
if diff == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
diffWithMs := NewUTXODiff()
|
|
||||||
for outpoint, entry := range diff.toAdd {
|
|
||||||
err := diffWithMs.AddEntry(outpoint, entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error with diffWithMs.AddEntry: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for outpoint, entry := range diff.toRemove {
|
|
||||||
err := diffWithMs.RemoveEntry(outpoint, entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error with diffWithMs.removeEntry: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return diffWithMs
|
|
||||||
}
|
|
||||||
|
|
||||||
func addMultisetToFullUTXOSet(t *testing.T, fus *FullUTXOSet) *FullUTXOSet {
|
|
||||||
if fus == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
fusWithMs := NewFullUTXOSet()
|
|
||||||
for outpoint, entry := range fus.utxoCollection {
|
|
||||||
err := fusWithMs.addAndUpdateMultiset(outpoint, entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error with diffWithMs.AddEntry: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fusWithMs
|
|
||||||
}
|
|
||||||
|
|
||||||
func addMultisetToDiffUTXOSet(t *testing.T, diffSet *DiffUTXOSet) *DiffUTXOSet {
|
|
||||||
if diffSet == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
diffWithMs := addMultisetToDiff(t, diffSet.UTXODiff)
|
|
||||||
baseWithMs := addMultisetToFullUTXOSet(t, diffSet.base)
|
|
||||||
return NewDiffUTXOSet(baseWithMs, diffWithMs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestFullUTXOSet makes sure that fullUTXOSet is working as expected.
|
// TestFullUTXOSet makes sure that fullUTXOSet is working as expected.
|
||||||
func TestFullUTXOSet(t *testing.T) {
|
func TestFullUTXOSet(t *testing.T) {
|
||||||
txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000")
|
txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000")
|
||||||
@ -703,10 +637,10 @@ func TestFullUTXOSet(t *testing.T) {
|
|||||||
txOut1 := &wire.TxOut{ScriptPubKey: []byte{}, Value: 20}
|
txOut1 := &wire.TxOut{ScriptPubKey: []byte{}, Value: 20}
|
||||||
utxoEntry0 := NewUTXOEntry(txOut0, true, 0)
|
utxoEntry0 := NewUTXOEntry(txOut0, true, 0)
|
||||||
utxoEntry1 := NewUTXOEntry(txOut1, false, 1)
|
utxoEntry1 := NewUTXOEntry(txOut1, false, 1)
|
||||||
diff := addMultisetToDiff(t, &UTXODiff{
|
diff := &UTXODiff{
|
||||||
toAdd: utxoCollection{outpoint0: utxoEntry0},
|
toAdd: utxoCollection{outpoint0: utxoEntry0},
|
||||||
toRemove: utxoCollection{outpoint1: utxoEntry1},
|
toRemove: utxoCollection{outpoint1: utxoEntry1},
|
||||||
})
|
}
|
||||||
|
|
||||||
// Test fullUTXOSet creation
|
// Test fullUTXOSet creation
|
||||||
emptySet := NewFullUTXOSet()
|
emptySet := NewFullUTXOSet()
|
||||||
@ -735,7 +669,7 @@ func TestFullUTXOSet(t *testing.T) {
|
|||||||
} else if isAccepted {
|
} else if isAccepted {
|
||||||
t.Errorf("addTx unexpectedly succeeded")
|
t.Errorf("addTx unexpectedly succeeded")
|
||||||
}
|
}
|
||||||
emptySet = addMultisetToFullUTXOSet(t, &FullUTXOSet{utxoCollection: utxoCollection{outpoint0: utxoEntry0}})
|
emptySet = &FullUTXOSet{utxoCollection: utxoCollection{outpoint0: utxoEntry0}}
|
||||||
if isAccepted, err := emptySet.AddTx(transaction0, 0); err != nil {
|
if isAccepted, err := emptySet.AddTx(transaction0, 0); err != nil {
|
||||||
t.Errorf("addTx unexpectedly failed. Error: %s", err)
|
t.Errorf("addTx unexpectedly failed. Error: %s", err)
|
||||||
} else if !isAccepted {
|
} else if !isAccepted {
|
||||||
@ -767,10 +701,10 @@ func TestDiffUTXOSet(t *testing.T) {
|
|||||||
txOut1 := &wire.TxOut{ScriptPubKey: []byte{}, Value: 20}
|
txOut1 := &wire.TxOut{ScriptPubKey: []byte{}, Value: 20}
|
||||||
utxoEntry0 := NewUTXOEntry(txOut0, true, 0)
|
utxoEntry0 := NewUTXOEntry(txOut0, true, 0)
|
||||||
utxoEntry1 := NewUTXOEntry(txOut1, false, 1)
|
utxoEntry1 := NewUTXOEntry(txOut1, false, 1)
|
||||||
diff := addMultisetToDiff(t, &UTXODiff{
|
diff := &UTXODiff{
|
||||||
toAdd: utxoCollection{outpoint0: utxoEntry0},
|
toAdd: utxoCollection{outpoint0: utxoEntry0},
|
||||||
toRemove: utxoCollection{outpoint1: utxoEntry1},
|
toRemove: utxoCollection{outpoint1: utxoEntry1},
|
||||||
})
|
}
|
||||||
|
|
||||||
// Test diffUTXOSet creation
|
// Test diffUTXOSet creation
|
||||||
emptySet := NewDiffUTXOSet(NewFullUTXOSet(), NewUTXODiff())
|
emptySet := NewDiffUTXOSet(NewFullUTXOSet(), NewUTXODiff())
|
||||||
@ -828,7 +762,7 @@ func TestDiffUTXOSet(t *testing.T) {
|
|||||||
toRemove: utxoCollection{},
|
toRemove: utxoCollection{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedString: "{Base: [ ], To Add: [ ], To Remove: [ ], Multiset-Hash:0000000000000000000000000000000000000000000000000000000000000000}",
|
expectedString: "{Base: [ ], To Add: [ ], To Remove: [ ]}",
|
||||||
expectedCollection: utxoCollection{},
|
expectedCollection: utxoCollection{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -847,7 +781,7 @@ func TestDiffUTXOSet(t *testing.T) {
|
|||||||
toRemove: utxoCollection{},
|
toRemove: utxoCollection{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedString: "{Base: [ ], To Add: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Remove: [ ], Multiset-Hash:2103b44fd413f5c28b5ad9afe7c3bebf19afae01cc202ea63b2f29d26252948d}",
|
expectedString: "{Base: [ ], To Add: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Remove: [ ]}",
|
||||||
expectedCollection: utxoCollection{outpoint0: utxoEntry0},
|
expectedCollection: utxoCollection{outpoint0: utxoEntry0},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -860,7 +794,7 @@ func TestDiffUTXOSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedMeldSet: nil,
|
expectedMeldSet: nil,
|
||||||
expectedString: "{Base: [ ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], Multiset-Hash:bae77d5370880c238265d07b80bdadbc6085990b2d2543a5f8beb2e3bd99b25b}",
|
expectedString: "{Base: [ ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]}",
|
||||||
expectedCollection: utxoCollection{},
|
expectedCollection: utxoCollection{},
|
||||||
expectedMeldToBaseError: "Couldn't remove outpoint 0000000000000000000000000000000000000000000000000000000000000000:0 because it doesn't exist in the DiffUTXOSet base",
|
expectedMeldToBaseError: "Couldn't remove outpoint 0000000000000000000000000000000000000000000000000000000000000000:0 because it doesn't exist in the DiffUTXOSet base",
|
||||||
},
|
},
|
||||||
@ -885,7 +819,7 @@ func TestDiffUTXOSet(t *testing.T) {
|
|||||||
toRemove: utxoCollection{},
|
toRemove: utxoCollection{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Add: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], To Remove: [ ], Multiset-Hash:c9932064f2f4ba1940d63b747e4a18053d0b022d66894a2ec1bbf88f74570e93}",
|
expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Add: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], To Remove: [ ]}",
|
||||||
expectedCollection: utxoCollection{
|
expectedCollection: utxoCollection{
|
||||||
outpoint0: utxoEntry0,
|
outpoint0: utxoEntry0,
|
||||||
outpoint1: utxoEntry1,
|
outpoint1: utxoEntry1,
|
||||||
@ -909,24 +843,21 @@ func TestDiffUTXOSet(t *testing.T) {
|
|||||||
toRemove: utxoCollection{},
|
toRemove: utxoCollection{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], Multiset-Hash:0000000000000000000000000000000000000000000000000000000000000000}",
|
expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]}",
|
||||||
expectedCollection: utxoCollection{},
|
expectedCollection: utxoCollection{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
diffSet := addMultisetToDiffUTXOSet(t, test.diffSet)
|
|
||||||
expectedMeldSet := addMultisetToDiffUTXOSet(t, test.expectedMeldSet)
|
|
||||||
|
|
||||||
// Test string representation
|
// Test string representation
|
||||||
setString := diffSet.String()
|
setString := test.diffSet.String()
|
||||||
if setString != test.expectedString {
|
if setString != test.expectedString {
|
||||||
t.Errorf("unexpected string in test \"%s\". "+
|
t.Errorf("unexpected string in test \"%s\". "+
|
||||||
"Expected: \"%s\", got: \"%s\".", test.name, test.expectedString, setString)
|
"Expected: \"%s\", got: \"%s\".", test.name, test.expectedString, setString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test meldToBase
|
// Test meldToBase
|
||||||
meldSet := diffSet.clone().(*DiffUTXOSet)
|
meldSet := test.diffSet.clone().(*DiffUTXOSet)
|
||||||
err := meldSet.meldToBase()
|
err := meldSet.meldToBase()
|
||||||
errString := ""
|
errString := ""
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -938,27 +869,27 @@ func TestDiffUTXOSet(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !meldSet.equal(expectedMeldSet) {
|
if !meldSet.equal(test.expectedMeldSet) {
|
||||||
t.Errorf("unexpected melded set in test \"%s\". "+
|
t.Errorf("unexpected melded set in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, expectedMeldSet, meldSet)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.expectedMeldSet, meldSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test collection
|
// Test collection
|
||||||
setCollection, err := diffSet.collection()
|
setCollection, err := test.diffSet.collection()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error getting diffSet collection: %s", err)
|
t.Errorf("Error getting test.diffSet collection: %s", err)
|
||||||
} else if !reflect.DeepEqual(setCollection, test.expectedCollection) {
|
} else if !reflect.DeepEqual(setCollection, test.expectedCollection) {
|
||||||
t.Errorf("unexpected set collection in test \"%s\". "+
|
t.Errorf("unexpected set collection in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, test.expectedCollection, setCollection)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.expectedCollection, setCollection)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test cloning
|
// Test cloning
|
||||||
clonedSet := diffSet.clone().(*DiffUTXOSet)
|
clonedSet := test.diffSet.clone().(*DiffUTXOSet)
|
||||||
if !reflect.DeepEqual(clonedSet, diffSet) {
|
if !reflect.DeepEqual(clonedSet, test.diffSet) {
|
||||||
t.Errorf("unexpected set clone in test \"%s\". "+
|
t.Errorf("unexpected set clone in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, diffSet, clonedSet)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.diffSet, clonedSet)
|
||||||
}
|
}
|
||||||
if clonedSet == diffSet {
|
if clonedSet == test.diffSet {
|
||||||
t.Errorf("cloned set is reference-equal to the original")
|
t.Errorf("cloned set is reference-equal to the original")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1159,10 +1090,7 @@ func TestDiffUTXOSet_addTx(t *testing.T) {
|
|||||||
|
|
||||||
testLoop:
|
testLoop:
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
startSet := addMultisetToDiffUTXOSet(t, test.startSet)
|
diffSet := test.startSet.clone()
|
||||||
expectedSet := addMultisetToDiffUTXOSet(t, test.expectedSet)
|
|
||||||
|
|
||||||
diffSet := startSet.clone()
|
|
||||||
|
|
||||||
// Apply all transactions to diffSet, in order, with the initial block height startHeight
|
// Apply all transactions to diffSet, in order, with the initial block height startHeight
|
||||||
for i, transaction := range test.toAdd {
|
for i, transaction := range test.toAdd {
|
||||||
@ -1174,18 +1102,18 @@ testLoop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the result diffSet equals to the expectedSet
|
// Make sure that the result diffSet equals to test.expectedSet
|
||||||
if !diffSet.(*DiffUTXOSet).equal(expectedSet) {
|
if !diffSet.(*DiffUTXOSet).equal(test.expectedSet) {
|
||||||
t.Errorf("unexpected diffSet in test \"%s\". "+
|
t.Errorf("unexpected diffSet in test \"%s\". "+
|
||||||
"Expected: \"%v\", got: \"%v\".", test.name, expectedSet, diffSet)
|
"Expected: \"%v\", got: \"%v\".", test.name, test.expectedSet, diffSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDiffFromTx(t *testing.T) {
|
func TestDiffFromTx(t *testing.T) {
|
||||||
fus := addMultisetToFullUTXOSet(t, &FullUTXOSet{
|
fus := &FullUTXOSet{
|
||||||
utxoCollection: utxoCollection{},
|
utxoCollection: utxoCollection{},
|
||||||
})
|
}
|
||||||
|
|
||||||
txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000")
|
txID0, _ := daghash.NewTxIDFromStr("0000000000000000000000000000000000000000000000000000000000000000")
|
||||||
txIn0 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: wire.Outpoint{TxID: *txID0, Index: math.MaxUint32}, Sequence: 0}
|
txIn0 := &wire.TxIn{SignatureScript: []byte{}, PreviousOutpoint: wire.Outpoint{TxID: *txID0, Index: math.MaxUint32}, Sequence: 0}
|
||||||
@ -1241,10 +1169,10 @@ func TestDiffFromTx(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Test that we get an error if the outpoint is inside diffUTXOSet's toRemove
|
//Test that we get an error if the outpoint is inside diffUTXOSet's toRemove
|
||||||
diff2 := addMultisetToDiff(t, &UTXODiff{
|
diff2 := &UTXODiff{
|
||||||
toAdd: utxoCollection{},
|
toAdd: utxoCollection{},
|
||||||
toRemove: utxoCollection{},
|
toRemove: utxoCollection{},
|
||||||
})
|
}
|
||||||
dus := NewDiffUTXOSet(fus, diff2)
|
dus := NewDiffUTXOSet(fus, diff2)
|
||||||
if isAccepted, err := dus.AddTx(tx, 2); err != nil {
|
if isAccepted, err := dus.AddTx(tx, 2); err != nil {
|
||||||
t.Fatalf("AddTx unexpectedly failed. Error: %s", err)
|
t.Fatalf("AddTx unexpectedly failed. Error: %s", err)
|
||||||
@ -1321,7 +1249,6 @@ func TestUTXOSetAddEntry(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
expectedUTXODiff := addMultisetToDiff(t, test.expectedUTXODiff)
|
|
||||||
err := utxoDiff.AddEntry(*test.outpointToAdd, test.utxoEntryToAdd)
|
err := utxoDiff.AddEntry(*test.outpointToAdd, test.utxoEntryToAdd)
|
||||||
errString := ""
|
errString := ""
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1330,9 +1257,9 @@ func TestUTXOSetAddEntry(t *testing.T) {
|
|||||||
if errString != test.expectedError {
|
if errString != test.expectedError {
|
||||||
t.Fatalf("utxoDiff.AddEntry: unexpected err in test \"%s\". Expected: %s but got: %s", test.name, test.expectedError, err)
|
t.Fatalf("utxoDiff.AddEntry: unexpected err in test \"%s\". Expected: %s but got: %s", test.name, test.expectedError, err)
|
||||||
}
|
}
|
||||||
if err == nil && !utxoDiff.equal(expectedUTXODiff) {
|
if err == nil && !utxoDiff.equal(test.expectedUTXODiff) {
|
||||||
t.Fatalf("utxoDiff.AddEntry: unexpected utxoDiff in test \"%s\". "+
|
t.Fatalf("utxoDiff.AddEntry: unexpected utxoDiff in test \"%s\". "+
|
||||||
"Expected: %v, got: %v", test.name, expectedUTXODiff, utxoDiff)
|
"Expected: %v, got: %v", test.name, test.expectedUTXODiff, utxoDiff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -454,7 +454,7 @@ func (mp *TxPool) HaveTransaction(hash *daghash.TxID) bool {
|
|||||||
//
|
//
|
||||||
// This function MUST be called with the mempool lock held (for writes).
|
// This function MUST be called with the mempool lock held (for writes).
|
||||||
func (mp *TxPool) removeTransactions(txs []*util.Tx) error {
|
func (mp *TxPool) removeTransactions(txs []*util.Tx) error {
|
||||||
diff := blockdag.NewUTXODiffWithoutMultiset()
|
diff := blockdag.NewUTXODiff()
|
||||||
|
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
txID := tx.ID()
|
txID := tx.ID()
|
||||||
@ -502,7 +502,7 @@ func (mp *TxPool) removeTransaction(tx *util.Tx, removeDependants bool, restoreI
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
diff := blockdag.NewUTXODiffWithoutMultiset()
|
diff := blockdag.NewUTXODiff()
|
||||||
err := mp.removeTransactionWithDiff(tx, diff, restoreInputs)
|
err := mp.removeTransactionWithDiff(tx, diff, restoreInputs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1353,7 +1353,7 @@ func (mp *TxPool) HandleNewBlock(block *util.Block, txChan chan NewBlockMsg) err
|
|||||||
// transactions until they are mined into a block.
|
// transactions until they are mined into a block.
|
||||||
func New(cfg *Config) *TxPool {
|
func New(cfg *Config) *TxPool {
|
||||||
virtualUTXO := cfg.DAG.UTXOSet()
|
virtualUTXO := cfg.DAG.UTXOSet()
|
||||||
mpUTXO := blockdag.NewDiffUTXOSet(virtualUTXO, blockdag.NewUTXODiffWithoutMultiset())
|
mpUTXO := blockdag.NewDiffUTXOSet(virtualUTXO, blockdag.NewUTXODiff())
|
||||||
return &TxPool{
|
return &TxPool{
|
||||||
cfg: *cfg,
|
cfg: *cfg,
|
||||||
pool: make(map[daghash.TxID]*TxDesc),
|
pool: make(map[daghash.TxID]*TxDesc),
|
||||||
|
@ -254,7 +254,7 @@ func (tc *testContext) mineTransactions(transactions []*util.Tx, numberOfBlocks
|
|||||||
if i == 0 {
|
if i == 0 {
|
||||||
blockTxs = msgTxs
|
blockTxs = msgTxs
|
||||||
}
|
}
|
||||||
block, err := mining.PrepareBlockForTest(tc.harness.txPool.cfg.DAG, tc.harness.txPool.cfg.DAGParams, tc.harness.txPool.cfg.DAG.TipHashes(), blockTxs, true)
|
block, err := mining.PrepareBlockForTest(tc.harness.txPool.cfg.DAG, tc.harness.txPool.cfg.DAGParams, tc.harness.txPool.cfg.DAG.TipHashes(), blockTxs, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Fatalf("PrepareBlockForTest: %s", err)
|
tc.t.Fatalf("PrepareBlockForTest: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -183,11 +183,11 @@ func NewBlkTmplGenerator(policy *Policy, params *dagconfig.Params,
|
|||||||
// | transactions (while block size | |
|
// | transactions (while block size | |
|
||||||
// | <= policy.BlockMinSize) | |
|
// | <= policy.BlockMinSize) | |
|
||||||
// ----------------------------------- --
|
// ----------------------------------- --
|
||||||
func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTemplate, error) {
|
func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address, extraNonce uint64) (*BlockTemplate, error) {
|
||||||
g.dag.Lock()
|
g.dag.Lock()
|
||||||
defer g.dag.Unlock()
|
defer g.dag.Unlock()
|
||||||
|
|
||||||
txsForBlockTemplate, err := g.selectTxs(payToAddress)
|
txsForBlockTemplate, err := g.selectTxs(payToAddress, extraNonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Errorf("failed to select transactions: %s", err)
|
return nil, errors.Errorf("failed to select transactions: %s", err)
|
||||||
}
|
}
|
||||||
@ -219,15 +219,6 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *BlkTmplGenerator) buildUTXOCommitment(transactions []*wire.MsgTx) (*daghash.Hash, error) {
|
|
||||||
utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, blockdag.UnacceptedBlueScore, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utxoWithTransactions.Multiset().Hash(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateBlockTime updates the timestamp in the header of the passed block to
|
// UpdateBlockTime updates the timestamp in the header of the passed block to
|
||||||
// the current time while taking into account the median time of the last
|
// the current time while taking into account the median time of the last
|
||||||
// several blocks to ensure the new time is after that time per the DAG
|
// several blocks to ensure the new time is after that time per the DAG
|
||||||
@ -244,59 +235,6 @@ func (g *BlkTmplGenerator) UpdateBlockTime(msgBlock *wire.MsgBlock) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateExtraNonce updates the extra nonce in the coinbase script of the passed
|
|
||||||
// block by regenerating the coinbase script with the passed value and block
|
|
||||||
// height. It also recalculates and updates the new merkle root that results
|
|
||||||
// from changing the coinbase script.
|
|
||||||
func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, extraNonce uint64) error {
|
|
||||||
coinbasePayloadScriptPubKey, _, err := blockdag.DeserializeCoinbasePayload(msgBlock.Transactions[util.CoinbaseTransactionIndex])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
coinbasePayloadExtraData, err := blockdag.CoinbasePayloadExtraData(extraNonce, CoinbaseFlags)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
coinbasePayload, err := blockdag.SerializeCoinbasePayload(coinbasePayloadScriptPubKey, coinbasePayloadExtraData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(coinbasePayload) > blockdag.MaxCoinbasePayloadLen {
|
|
||||||
return errors.Errorf("coinbase transaction script length "+
|
|
||||||
"of %d is out of range (max: %d)",
|
|
||||||
len(coinbasePayload),
|
|
||||||
blockdag.MaxCoinbasePayloadLen)
|
|
||||||
}
|
|
||||||
oldCoinbaseTx := msgBlock.Transactions[util.CoinbaseTransactionIndex]
|
|
||||||
msgBlock.Transactions[util.CoinbaseTransactionIndex] = wire.NewSubnetworkMsgTx(oldCoinbaseTx.Version, oldCoinbaseTx.TxIn, oldCoinbaseTx.TxOut, &oldCoinbaseTx.SubnetworkID, oldCoinbaseTx.Gas, coinbasePayload)
|
|
||||||
|
|
||||||
// TODO(davec): A util.Block should use saved in the state to avoid
|
|
||||||
// recalculating all of the other transaction hashes.
|
|
||||||
// block.Transactions[util.CoinbaseTransactionIndex].InvalidateCache()
|
|
||||||
|
|
||||||
// Recalculate the merkle roots with the updated extra nonce.
|
|
||||||
block := util.NewBlock(msgBlock)
|
|
||||||
hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions())
|
|
||||||
msgBlock.Header.HashMerkleRoot = hashMerkleTree.Root()
|
|
||||||
|
|
||||||
// buildUTXOCommitment is the only function in UpdateExtraNonce that
|
|
||||||
// requires the dagLock, and as such we lock and unlock it locally.
|
|
||||||
utxoCommitment, err := func() (*daghash.Hash, error) {
|
|
||||||
g.dag.Lock()
|
|
||||||
defer g.dag.Unlock()
|
|
||||||
|
|
||||||
return g.buildUTXOCommitment(msgBlock.Transactions)
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msgBlock.Header.UTXOCommitment = utxoCommitment
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxSource returns the associated transaction source.
|
// TxSource returns the associated transaction source.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
|
@ -68,13 +68,11 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
template, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
|
// We create a deterministic extra nonce in order of
|
||||||
if err != nil {
|
// creating deterministic coinbase tx ids.
|
||||||
return nil, err
|
extraNonce := GenerateDeterministicExtraNonceForTest()
|
||||||
}
|
|
||||||
|
|
||||||
// In order of creating deterministic coinbase tx ids.
|
template, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr, extraNonce)
|
||||||
err = blockTemplateGenerator.UpdateExtraNonce(template.Block, GenerateDeterministicExtraNonceForTest())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -106,10 +104,12 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
|
|||||||
}
|
}
|
||||||
template.Block.Header.HashMerkleRoot = blockdag.BuildHashMerkleTreeStore(utilTxs).Root()
|
template.Block.Header.HashMerkleRoot = blockdag.BuildHashMerkleTreeStore(utilTxs).Root()
|
||||||
|
|
||||||
template.Block.Header.UTXOCommitment, err = blockTemplateGenerator.buildUTXOCommitment(template.Block.Transactions)
|
ms, err := dag.NextBlockMultiset(utilTxs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template.Block.Header.UTXOCommitment = ms.Hash()
|
||||||
}
|
}
|
||||||
return template.Block, nil
|
return template.Block, nil
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package mining
|
|||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/blockdag"
|
"github.com/kaspanet/kaspad/blockdag"
|
||||||
"github.com/kaspanet/kaspad/util"
|
"github.com/kaspanet/kaspad/util"
|
||||||
"github.com/kaspanet/kaspad/util/random"
|
|
||||||
"github.com/kaspanet/kaspad/util/subnetworkid"
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -65,13 +64,13 @@ type txsForBlockTemplate struct {
|
|||||||
// Once the sum of probabilities of marked transactions is greater than
|
// Once the sum of probabilities of marked transactions is greater than
|
||||||
// rebalanceThreshold percent of the sum of probabilities of all transactions,
|
// rebalanceThreshold percent of the sum of probabilities of all transactions,
|
||||||
// rebalance.
|
// rebalance.
|
||||||
func (g *BlkTmplGenerator) selectTxs(payToAddress util.Address) (*txsForBlockTemplate, error) {
|
func (g *BlkTmplGenerator) selectTxs(payToAddress util.Address, extraNonce uint64) (*txsForBlockTemplate, error) {
|
||||||
// Fetch the source transactions.
|
// Fetch the source transactions.
|
||||||
sourceTxs := g.txSource.MiningDescs()
|
sourceTxs := g.txSource.MiningDescs()
|
||||||
|
|
||||||
// Create a new txsForBlockTemplate struct, onto which all selectedTxs
|
// Create a new txsForBlockTemplate struct, onto which all selectedTxs
|
||||||
// will be appended.
|
// will be appended.
|
||||||
txsForBlockTemplate, err := g.newTxsForBlockTemplate(payToAddress, sourceTxs)
|
txsForBlockTemplate, err := g.newTxsForBlockTemplate(payToAddress, extraNonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -91,7 +90,7 @@ func (g *BlkTmplGenerator) selectTxs(payToAddress util.Address) (*txsForBlockTem
|
|||||||
|
|
||||||
// newTxsForBlockTemplate creates a txsForBlockTemplate and initializes it
|
// newTxsForBlockTemplate creates a txsForBlockTemplate and initializes it
|
||||||
// with a coinbase transaction.
|
// with a coinbase transaction.
|
||||||
func (g *BlkTmplGenerator) newTxsForBlockTemplate(payToAddress util.Address, sourceTxs []*TxDesc) (*txsForBlockTemplate, error) {
|
func (g *BlkTmplGenerator) newTxsForBlockTemplate(payToAddress util.Address, extraNonce uint64) (*txsForBlockTemplate, error) {
|
||||||
// Create a new txsForBlockTemplate struct. The struct holds the mass,
|
// Create a new txsForBlockTemplate struct. The struct holds the mass,
|
||||||
// the fees, and number of signature operations for each of the selected
|
// the fees, and number of signature operations for each of the selected
|
||||||
// transactions and adds an entry for the coinbase. This allows the code
|
// transactions and adds an entry for the coinbase. This allows the code
|
||||||
@ -103,10 +102,6 @@ func (g *BlkTmplGenerator) newTxsForBlockTemplate(payToAddress util.Address, sou
|
|||||||
txFees: make([]uint64, 0),
|
txFees: make([]uint64, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
extraNonce, err := random.Uint64()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
coinbasePayloadExtraData, err := blockdag.CoinbasePayloadExtraData(extraNonce, CoinbaseFlags)
|
coinbasePayloadExtraData, err := blockdag.CoinbasePayloadExtraData(extraNonce, CoinbaseFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -108,7 +108,6 @@ type GetBlockDAGInfoResult struct {
|
|||||||
TipHashes []string `json:"tipHashes"`
|
TipHashes []string `json:"tipHashes"`
|
||||||
Difficulty float64 `json:"difficulty"`
|
Difficulty float64 `json:"difficulty"`
|
||||||
MedianTime int64 `json:"medianTime"`
|
MedianTime int64 `json:"medianTime"`
|
||||||
UTXOCommitment string `json:"utxoCommitment"`
|
|
||||||
VerificationProgress float64 `json:"verificationProgress,omitempty"`
|
VerificationProgress float64 `json:"verificationProgress,omitempty"`
|
||||||
Pruned bool `json:"pruned"`
|
Pruned bool `json:"pruned"`
|
||||||
PruneHeight uint64 `json:"pruneHeight,omitempty"`
|
PruneHeight uint64 `json:"pruneHeight,omitempty"`
|
||||||
|
@ -24,7 +24,6 @@ func handleGetBlockDAGInfo(s *Server, cmd interface{}, closeChan <-chan struct{}
|
|||||||
TipHashes: daghash.Strings(dag.TipHashes()),
|
TipHashes: daghash.Strings(dag.TipHashes()),
|
||||||
Difficulty: getDifficultyRatio(dag.CurrentBits(), params),
|
Difficulty: getDifficultyRatio(dag.CurrentBits(), params),
|
||||||
MedianTime: dag.CalcPastMedianTime().Unix(),
|
MedianTime: dag.CalcPastMedianTime().Unix(),
|
||||||
UTXOCommitment: dag.UTXOCommitment(),
|
|
||||||
Pruned: false,
|
Pruned: false,
|
||||||
Bip9SoftForks: make(map[string]*rpcmodel.Bip9SoftForkDescription),
|
Bip9SoftForks: make(map[string]*rpcmodel.Bip9SoftForkDescription),
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/txscript"
|
"github.com/kaspanet/kaspad/txscript"
|
||||||
"github.com/kaspanet/kaspad/util"
|
"github.com/kaspanet/kaspad/util"
|
||||||
"github.com/kaspanet/kaspad/util/daghash"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/util/random"
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -616,10 +617,17 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool)
|
|||||||
// block template doesn't include the coinbase, so the caller
|
// block template doesn't include the coinbase, so the caller
|
||||||
// will ultimately create their own coinbase which pays to the
|
// will ultimately create their own coinbase which pays to the
|
||||||
// appropriate address(es).
|
// appropriate address(es).
|
||||||
blkTemplate, err := generator.NewBlockTemplate(payAddr)
|
|
||||||
|
extraNonce, err := random.Uint64()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return internalRPCError("Failed to create new block "+
|
return internalRPCError(fmt.Sprintf("Failed to randomize "+
|
||||||
"template: "+err.Error(), "")
|
"extra nonce: %s", err.Error()), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
blkTemplate, err := generator.NewBlockTemplate(payAddr, extraNonce)
|
||||||
|
if err != nil {
|
||||||
|
return internalRPCError(fmt.Sprintf("Failed to create new block "+
|
||||||
|
"template: %s", err.Error()), "")
|
||||||
}
|
}
|
||||||
template = blkTemplate
|
template = blkTemplate
|
||||||
msgBlock = template.Block
|
msgBlock = template.Block
|
||||||
|
Loading…
x
Reference in New Issue
Block a user