mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-07 14:46:44 +00:00
Implement new mechanism for updating UTXO Diffs (#1671)
* Use selectedParent instead of selectedTip for non-selectedTip blocks in restoreSingleBlockStatus * Cache the selectedParent for re-use in a resolveSingleBlockStatus chain * Implement and use reverseUTXOSet * Reverse blocks in correct order * Support resolveBlockStatus without separate stagingAreas for usage of testConsensus * Handle the case where the tip of the resolved block is not the next selectedTip * Unify isResolveTip * Some minor fixes and cleanup * Add full finality window re-org test to stability-slow * rename: useSeparateStagingAreasPerBlock -> useSeparateStagingAreaPerBlock * Better logs in resolveSingleBlockStatus * A few retouches to reverseUTXODiffs * TEMPORARY COMMIT: EXTRAT ALL DIFFFROMS TO SEPARATE METHODS * TEMPORARY COMMIT: REMOVE DIFFICULTY CHECKS IN DEVNET * Don't pre-allocate in utxo-algebra, since the numbers are not known ahead-of-time * Add some logs to reverseUTXODiffs * Revert "TEMPORARY COMMIT: REMOVE DIFFICULTY CHECKS IN DEVNET" This reverts commit c0af9dc6ade78a914c970e11bc63c34605565f57. * Revert "TEMPORARY COMMIT: EXTRAT ALL DIFFFROMS TO SEPARATE METHODS" This reverts commit 4fcca1b48c3a1183598833a355b9bfaf169edba1. * Remove redundant paranthesis * Revise some logs messages * Rename:oneBlockBeforeCurrentUTXOSet -> lastResolvedBlockUTXOSet * Don't break if the block was resolved as invalid * rename unverifiedBlocks to recentlyVerifiedBlcks in reverseUTXODiffs * Add errors.New to the panic, for a stack trace * Reverse the UTXODiffs after the main block has been commited * Use the correct value for previousUTXODiff * Add test for ReverseUTXODiff * Fix some names and comments * Update TestReverseUTXODiffs to use consensus.Config * Fix comments mentioning 'oneBlockBeforeTip'
This commit is contained in:
parent
28bfc0fb9c
commit
dfd8b3423d
@ -28,7 +28,9 @@ func New(cacheSize int, preallocate bool) model.UTXODiffStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stage stages the given utxoDiff for the given blockHash
|
// Stage stages the given utxoDiff for the given blockHash
|
||||||
func (uds *utxoDiffStore) Stage(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, utxoDiff externalapi.UTXODiff, utxoDiffChild *externalapi.DomainHash) {
|
func (uds *utxoDiffStore) Stage(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||||
|
utxoDiff externalapi.UTXODiff, utxoDiffChild *externalapi.DomainHash) {
|
||||||
|
|
||||||
stagingShard := uds.stagingShard(stagingArea)
|
stagingShard := uds.stagingShard(stagingArea)
|
||||||
|
|
||||||
stagingShard.utxoDiffToAdd[*blockHash] = utxoDiff
|
stagingShard.utxoDiffToAdd[*blockHash] = utxoDiff
|
||||||
|
@ -67,7 +67,7 @@ func TestFinality(t *testing.T) {
|
|||||||
for i := uint64(0); i < finalityInterval-2; i++ {
|
for i := uint64(0); i < finalityInterval-2; i++ {
|
||||||
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %+v", i, err)
|
||||||
}
|
}
|
||||||
sideChainTipHash = consensushashing.BlockHash(sideChainTip)
|
sideChainTipHash = consensushashing.BlockHash(sideChainTip)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ type UTXODiff interface {
|
|||||||
ToRemove() UTXOCollection
|
ToRemove() UTXOCollection
|
||||||
WithDiff(other UTXODiff) (UTXODiff, error)
|
WithDiff(other UTXODiff) (UTXODiff, error)
|
||||||
DiffFrom(other UTXODiff) (UTXODiff, error)
|
DiffFrom(other UTXODiff) (UTXODiff, error)
|
||||||
|
Reversed() UTXODiff
|
||||||
CloneMutable() MutableUTXODiff
|
CloneMutable() MutableUTXODiff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,12 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|||||||
|
|
||||||
// ConsensusStateManager manages the node's consensus state
|
// ConsensusStateManager manages the node's consensus state
|
||||||
type ConsensusStateManager interface {
|
type ConsensusStateManager interface {
|
||||||
AddBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, externalapi.UTXODiff, error)
|
AddBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, externalapi.UTXODiff, *UTXODiffReversalData, error)
|
||||||
PopulateTransactionWithUTXOEntries(stagingArea *StagingArea, transaction *externalapi.DomainTransaction) error
|
PopulateTransactionWithUTXOEntries(stagingArea *StagingArea, transaction *externalapi.DomainTransaction) error
|
||||||
ImportPruningPoint(stagingArea *StagingArea, newPruningPoint *externalapi.DomainBlock) error
|
ImportPruningPoint(stagingArea *StagingArea, newPruningPoint *externalapi.DomainBlock) error
|
||||||
RestorePastUTXOSetIterator(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (externalapi.ReadOnlyUTXOSetIterator, error)
|
RestorePastUTXOSetIterator(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (externalapi.ReadOnlyUTXOSetIterator, error)
|
||||||
CalculatePastUTXOAndAcceptanceData(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (externalapi.UTXODiff, externalapi.AcceptanceData, Multiset, error)
|
CalculatePastUTXOAndAcceptanceData(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (externalapi.UTXODiff, externalapi.AcceptanceData, Multiset, error)
|
||||||
GetVirtualSelectedParentChainFromBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, error)
|
GetVirtualSelectedParentChainFromBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, error)
|
||||||
RecoverUTXOIfRequired() error
|
RecoverUTXOIfRequired() error
|
||||||
|
ReverseUTXODiffs(tipHash *externalapi.DomainHash, reversalData *UTXODiffReversalData) error
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,5 @@ type TestConsensusStateManager interface {
|
|||||||
AddUTXOToMultiset(multiset model.Multiset, entry externalapi.UTXOEntry,
|
AddUTXOToMultiset(multiset model.Multiset, entry externalapi.UTXOEntry,
|
||||||
outpoint *externalapi.DomainOutpoint) error
|
outpoint *externalapi.DomainOutpoint) error
|
||||||
ResolveBlockStatus(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
ResolveBlockStatus(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||||
useSeparateStagingAreasPerBlock bool) (externalapi.BlockStatus, error)
|
useSeparateStagingAreaPerBlock bool) (externalapi.BlockStatus, error)
|
||||||
}
|
}
|
||||||
|
9
domain/consensus/model/utxo_diff_reversal_data.go
Normal file
9
domain/consensus/model/utxo_diff_reversal_data.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
|
||||||
|
// UTXODiffReversalData is used by ConsensusStateManager to reverse the UTXODiffs during a re-org
|
||||||
|
type UTXODiffReversalData struct {
|
||||||
|
SelectedParentHash *externalapi.DomainHash
|
||||||
|
SelectedParentUTXODiff externalapi.UTXODiff
|
||||||
|
}
|
@ -90,6 +90,7 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
|||||||
|
|
||||||
var selectedParentChainChanges *externalapi.SelectedChainPath
|
var selectedParentChainChanges *externalapi.SelectedChainPath
|
||||||
var virtualUTXODiff externalapi.UTXODiff
|
var virtualUTXODiff externalapi.UTXODiff
|
||||||
|
var reversalData *model.UTXODiffReversalData
|
||||||
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
|
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
|
||||||
if !isHeaderOnlyBlock {
|
if !isHeaderOnlyBlock {
|
||||||
// There's no need to update the consensus state manager when
|
// There's no need to update the consensus state manager when
|
||||||
@ -97,7 +98,7 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
|||||||
// in consensusStateManager.ImportPruningPoint
|
// in consensusStateManager.ImportPruningPoint
|
||||||
if !isPruningPoint {
|
if !isPruningPoint {
|
||||||
// Attempt to add the block to the virtual
|
// Attempt to add the block to the virtual
|
||||||
selectedParentChainChanges, virtualUTXODiff, err = bp.consensusStateManager.AddBlock(stagingArea, blockHash)
|
selectedParentChainChanges, virtualUTXODiff, reversalData, err = bp.consensusStateManager.AddBlock(stagingArea, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -124,6 +125,13 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if reversalData != nil {
|
||||||
|
err = bp.consensusStateManager.ReverseUTXODiffs(blockHash, reversalData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = bp.pruningManager.UpdatePruningPointIfRequired()
|
err = bp.pruningManager.UpdatePruningPointIfRequired()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
// current virtual. This process may result in a new virtual block
|
// current virtual. This process may result in a new virtual block
|
||||||
// getting created
|
// getting created
|
||||||
func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (
|
func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (
|
||||||
*externalapi.SelectedChainPath, externalapi.UTXODiff, error) {
|
*externalapi.SelectedChainPath, externalapi.UTXODiff, *model.UTXODiffReversalData, error) {
|
||||||
|
|
||||||
onEnd := logger.LogAndMeasureExecutionTime(log, "csm.AddBlock")
|
onEnd := logger.LogAndMeasureExecutionTime(log, "csm.AddBlock")
|
||||||
defer onEnd()
|
defer onEnd()
|
||||||
@ -18,9 +18,10 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
|||||||
log.Debugf("Resolving whether the block %s is the next virtual selected parent", blockHash)
|
log.Debugf("Resolving whether the block %s is the next virtual selected parent", blockHash)
|
||||||
isCandidateToBeNextVirtualSelectedParent, err := csm.isCandidateToBeNextVirtualSelectedParent(stagingArea, blockHash)
|
isCandidateToBeNextVirtualSelectedParent, err := csm.isCandidateToBeNextVirtualSelectedParent(stagingArea, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reversalData *model.UTXODiffReversalData
|
||||||
if isCandidateToBeNextVirtualSelectedParent {
|
if isCandidateToBeNextVirtualSelectedParent {
|
||||||
// It's important to check for finality violation before resolving the block status, because the status of
|
// It's important to check for finality violation before resolving the block status, because the status of
|
||||||
// blocks with a selected chain that doesn't contain the pruning point cannot be resolved because they will
|
// blocks with a selected chain that doesn't contain the pruning point cannot be resolved because they will
|
||||||
@ -29,7 +30,7 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
|||||||
"finality", blockHash)
|
"finality", blockHash)
|
||||||
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(stagingArea, blockHash)
|
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(stagingArea, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldNotify {
|
if shouldNotify {
|
||||||
@ -39,9 +40,10 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
|||||||
|
|
||||||
if !isViolatingFinality {
|
if !isViolatingFinality {
|
||||||
log.Debugf("Block %s doesn't violate finality. Resolving its block status", blockHash)
|
log.Debugf("Block %s doesn't violate finality. Resolving its block status", blockHash)
|
||||||
blockStatus, err := csm.resolveBlockStatus(stagingArea, blockHash, true)
|
var blockStatus externalapi.BlockStatus
|
||||||
|
blockStatus, reversalData, err = csm.resolveBlockStatus(stagingArea, blockHash, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Block %s resolved to status `%s`", blockHash, blockStatus)
|
log.Debugf("Block %s resolved to status `%s`", blockHash, blockStatus)
|
||||||
@ -54,17 +56,17 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
|||||||
log.Debugf("Adding block %s to the DAG tips", blockHash)
|
log.Debugf("Adding block %s to the DAG tips", blockHash)
|
||||||
newTips, err := csm.addTip(stagingArea, blockHash)
|
newTips, err := csm.addTip(stagingArea, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("After adding %s, the amount of new tips are %d", blockHash, len(newTips))
|
log.Debugf("After adding %s, the amount of new tips are %d", blockHash, len(newTips))
|
||||||
|
|
||||||
log.Debugf("Updating the virtual with the new tips")
|
log.Debugf("Updating the virtual with the new tips")
|
||||||
selectedParentChainChanges, virtualUTXODiff, err := csm.updateVirtual(stagingArea, blockHash, newTips)
|
selectedParentChainChanges, virtualUTXODiff, err := csm.updateVirtual(stagingArea, blockHash, newTips)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectedParentChainChanges, virtualUTXODiff, nil
|
return selectedParentChainChanges, virtualUTXODiff, reversalData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) isCandidateToBeNextVirtualSelectedParent(
|
func (csm *consensusStateManager) isCandidateToBeNextVirtualSelectedParent(
|
||||||
|
@ -39,14 +39,26 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(stagingArea
|
|||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
daaScore, err := csm.daaBlocksStore.DAAScore(csm.databaseContext, stagingArea, blockHash)
|
log.Debugf("Restored the past UTXO of block %s with selectedParent %s. "+
|
||||||
|
"Diff toAdd length: %d, toRemove length: %d", blockHash, blockGHOSTDAGData.SelectedParent(),
|
||||||
|
selectedParentPastUTXO.ToAdd().Len(), selectedParentPastUTXO.ToRemove().Len())
|
||||||
|
|
||||||
|
return csm.calculatePastUTXOAndAcceptanceDataWithSelectedParentUTXO(stagingArea, blockHash, selectedParentPastUTXO)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csm *consensusStateManager) calculatePastUTXOAndAcceptanceDataWithSelectedParentUTXO(stagingArea *model.StagingArea,
|
||||||
|
blockHash *externalapi.DomainHash, selectedParentPastUTXO externalapi.UTXODiff) (
|
||||||
|
externalapi.UTXODiff, externalapi.AcceptanceData, model.Multiset, error) {
|
||||||
|
|
||||||
|
blockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Restored the past UTXO of block %s with selectedParent %s. "+
|
daaScore, err := csm.daaBlocksStore.DAAScore(csm.databaseContext, stagingArea, blockHash)
|
||||||
"Diff toAdd length: %d, toRemove length: %d", blockHash, blockGHOSTDAGData.SelectedParent(),
|
if err != nil {
|
||||||
selectedParentPastUTXO.ToAdd().Len(), selectedParentPastUTXO.ToRemove().Len())
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
log.Debugf("Applying blue blocks to the selected parent past UTXO of block %s", blockHash)
|
log.Debugf("Applying blue blocks to the selected parent past UTXO of block %s", blockHash)
|
||||||
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(
|
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(
|
||||||
@ -66,7 +78,7 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(stagingArea
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) restorePastUTXO(
|
func (csm *consensusStateManager) restorePastUTXO(
|
||||||
stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (externalapi.MutableUTXODiff, error) {
|
stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (externalapi.UTXODiff, error) {
|
||||||
|
|
||||||
onEnd := logger.LogAndMeasureExecutionTime(log, "restorePastUTXO")
|
onEnd := logger.LogAndMeasureExecutionTime(log, "restorePastUTXO")
|
||||||
defer onEnd()
|
defer onEnd()
|
||||||
@ -120,11 +132,11 @@ func (csm *consensusStateManager) restorePastUTXO(
|
|||||||
}
|
}
|
||||||
log.Tracef("The accumulated diff for block %s is: %s", blockHash, accumulatedDiff)
|
log.Tracef("The accumulated diff for block %s is: %s", blockHash, accumulatedDiff)
|
||||||
|
|
||||||
return accumulatedDiff, nil
|
return accumulatedDiff.ToImmutable(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) applyMergeSetBlocks(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
func (csm *consensusStateManager) applyMergeSetBlocks(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||||
selectedParentPastUTXODiff externalapi.MutableUTXODiff, ghostdagData *model.BlockGHOSTDAGData, daaScore uint64) (
|
selectedParentPastUTXODiff externalapi.UTXODiff, ghostdagData *model.BlockGHOSTDAGData, daaScore uint64) (
|
||||||
externalapi.AcceptanceData, externalapi.MutableUTXODiff, error) {
|
externalapi.AcceptanceData, externalapi.MutableUTXODiff, error) {
|
||||||
|
|
||||||
log.Debugf("applyMergeSetBlocks start for block %s", blockHash)
|
log.Debugf("applyMergeSetBlocks start for block %s", blockHash)
|
||||||
@ -144,7 +156,7 @@ func (csm *consensusStateManager) applyMergeSetBlocks(stagingArea *model.Staging
|
|||||||
log.Tracef("The past median time for block %s is: %d", blockHash, selectedParentMedianTime)
|
log.Tracef("The past median time for block %s is: %d", blockHash, selectedParentMedianTime)
|
||||||
|
|
||||||
multiblockAcceptanceData := make(externalapi.AcceptanceData, len(mergeSetBlocks))
|
multiblockAcceptanceData := make(externalapi.AcceptanceData, len(mergeSetBlocks))
|
||||||
accumulatedUTXODiff := selectedParentPastUTXODiff
|
accumulatedUTXODiff := selectedParentPastUTXODiff.CloneMutable()
|
||||||
accumulatedMass := uint64(0)
|
accumulatedMass := uint64(0)
|
||||||
|
|
||||||
for i, mergeSetBlock := range mergeSetBlocks {
|
for i, mergeSetBlock := range mergeSetBlocks {
|
||||||
@ -277,12 +289,13 @@ func (csm *consensusStateManager) checkTransactionMass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RestorePastUTXOSetIterator restores the given block's UTXOSet iterator, and returns it as a externalapi.ReadOnlyUTXOSetIterator
|
// RestorePastUTXOSetIterator restores the given block's UTXOSet iterator, and returns it as a externalapi.ReadOnlyUTXOSetIterator
|
||||||
func (csm *consensusStateManager) RestorePastUTXOSetIterator(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (externalapi.ReadOnlyUTXOSetIterator, error) {
|
func (csm *consensusStateManager) RestorePastUTXOSetIterator(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (
|
||||||
|
externalapi.ReadOnlyUTXOSetIterator, error) {
|
||||||
|
|
||||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RestorePastUTXOSetIterator")
|
onEnd := logger.LogAndMeasureExecutionTime(log, "RestorePastUTXOSetIterator")
|
||||||
defer onEnd()
|
defer onEnd()
|
||||||
|
|
||||||
blockStatus, err := csm.resolveBlockStatus(stagingArea, blockHash, true)
|
blockStatus, _, err := csm.resolveBlockStatus(stagingArea, blockHash, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -306,5 +319,5 @@ func (csm *consensusStateManager) RestorePastUTXOSetIterator(stagingArea *model.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return utxo.IteratorWithDiff(virtualUTXOSetIterator, blockDiff.ToImmutable())
|
return utxo.IteratorWithDiff(virtualUTXOSetIterator, blockDiff)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package consensusstatemanager
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/util/staging"
|
"github.com/kaspanet/kaspad/util/staging"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
@ -13,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (csm *consensusStateManager) resolveBlockStatus(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
func (csm *consensusStateManager) resolveBlockStatus(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||||
useSeparateStagingAreasPerBlock bool) (externalapi.BlockStatus, error) {
|
useSeparateStagingAreaPerBlock bool) (externalapi.BlockStatus, *model.UTXODiffReversalData, error) {
|
||||||
|
|
||||||
onEnd := logger.LogAndMeasureExecutionTime(log, fmt.Sprintf("resolveBlockStatus for %s", blockHash))
|
onEnd := logger.LogAndMeasureExecutionTime(log, fmt.Sprintf("resolveBlockStatus for %s", blockHash))
|
||||||
defer onEnd()
|
defer onEnd()
|
||||||
@ -22,7 +24,7 @@ func (csm *consensusStateManager) resolveBlockStatus(stagingArea *model.StagingA
|
|||||||
"parent chain of %s that have no yet resolved their status", blockHash)
|
"parent chain of %s that have no yet resolved their status", blockHash)
|
||||||
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(stagingArea, blockHash)
|
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(stagingArea, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("Got %d unverified blocks in the selected parent "+
|
log.Debugf("Got %d unverified blocks in the selected parent "+
|
||||||
"chain of %s: %s", len(unverifiedBlocks), blockHash, unverifiedBlocks)
|
"chain of %s: %s", len(unverifiedBlocks), blockHash, unverifiedBlocks)
|
||||||
@ -34,26 +36,33 @@ func (csm *consensusStateManager) resolveBlockStatus(stagingArea *model.StagingA
|
|||||||
"This means that the block already has a UTXO-verified status.", blockHash)
|
"This means that the block already has a UTXO-verified status.", blockHash)
|
||||||
status, err := csm.blockStatusStore.Get(csm.databaseContext, stagingArea, blockHash)
|
status, err := csm.blockStatusStore.Get(csm.databaseContext, stagingArea, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("Block %s's status resolved to: %s", blockHash, status)
|
log.Debugf("Block %s's status resolved to: %s", blockHash, status)
|
||||||
return status, nil
|
return status, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Finding the status of the selected parent of %s", blockHash)
|
log.Debugf("Finding the status of the selected parent of %s", blockHash)
|
||||||
selectedParentStatus, err := csm.findSelectedParentStatus(stagingArea, unverifiedBlocks)
|
selectedParentHash, selectedParentStatus, selectedParentUTXOSet, err := csm.selectedParentInfo(stagingArea, unverifiedBlocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("The status of the selected parent of %s is: %s", blockHash, selectedParentStatus)
|
log.Debugf("The status of the selected parent of %s is: %s", blockHash, selectedParentStatus)
|
||||||
|
|
||||||
log.Debugf("Resolving the unverified blocks' status in reverse order (past to present)")
|
log.Debugf("Resolving the unverified blocks' status in reverse order (past to present)")
|
||||||
var blockStatus externalapi.BlockStatus
|
var blockStatus externalapi.BlockStatus
|
||||||
|
|
||||||
|
previousBlockHash := selectedParentHash
|
||||||
|
previousBlockUTXOSet := selectedParentUTXOSet
|
||||||
|
var oneBeforeLastResolvedBlockUTXOSet externalapi.UTXODiff
|
||||||
|
var oneBeforeLastResolvedBlockHash *externalapi.DomainHash
|
||||||
|
|
||||||
for i := len(unverifiedBlocks) - 1; i >= 0; i-- {
|
for i := len(unverifiedBlocks) - 1; i >= 0; i-- {
|
||||||
unverifiedBlockHash := unverifiedBlocks[i]
|
unverifiedBlockHash := unverifiedBlocks[i]
|
||||||
|
|
||||||
stagingAreaForCurrentBlock := stagingArea
|
stagingAreaForCurrentBlock := stagingArea
|
||||||
useSeparateStagingArea := useSeparateStagingAreasPerBlock && (i != 0)
|
isResolveTip := i == 0
|
||||||
|
useSeparateStagingArea := useSeparateStagingAreaPerBlock && !isResolveTip
|
||||||
if useSeparateStagingArea {
|
if useSeparateStagingArea {
|
||||||
stagingAreaForCurrentBlock = model.NewStagingArea()
|
stagingAreaForCurrentBlock = model.NewStagingArea()
|
||||||
}
|
}
|
||||||
@ -61,9 +70,13 @@ func (csm *consensusStateManager) resolveBlockStatus(stagingArea *model.StagingA
|
|||||||
if selectedParentStatus == externalapi.StatusDisqualifiedFromChain {
|
if selectedParentStatus == externalapi.StatusDisqualifiedFromChain {
|
||||||
blockStatus = externalapi.StatusDisqualifiedFromChain
|
blockStatus = externalapi.StatusDisqualifiedFromChain
|
||||||
} else {
|
} else {
|
||||||
blockStatus, err = csm.resolveSingleBlockStatus(stagingAreaForCurrentBlock, unverifiedBlockHash)
|
oneBeforeLastResolvedBlockUTXOSet = previousBlockUTXOSet
|
||||||
|
oneBeforeLastResolvedBlockHash = previousBlockHash
|
||||||
|
|
||||||
|
blockStatus, previousBlockUTXOSet, err = csm.resolveSingleBlockStatus(
|
||||||
|
stagingAreaForCurrentBlock, unverifiedBlockHash, previousBlockHash, previousBlockUTXOSet, isResolveTip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,17 +88,39 @@ func (csm *consensusStateManager) resolveBlockStatus(stagingArea *model.StagingA
|
|||||||
if useSeparateStagingArea {
|
if useSeparateStagingArea {
|
||||||
err := staging.CommitAllChanges(csm.databaseContext, stagingAreaForCurrentBlock)
|
err := staging.CommitAllChanges(csm.databaseContext, stagingAreaForCurrentBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
previousBlockHash = unverifiedBlockHash
|
||||||
|
}
|
||||||
|
|
||||||
|
var reversalData *model.UTXODiffReversalData
|
||||||
|
if blockStatus == externalapi.StatusUTXOValid && len(unverifiedBlocks) > 1 {
|
||||||
|
log.Debugf("Preparing data for reversing the UTXODiff")
|
||||||
|
// During resolveSingleBlockStatus, all unverifiedBlocks (excluding the tip) were assigned their selectedParent
|
||||||
|
// as their UTXODiffChild.
|
||||||
|
// Now that the whole chain has been resolved - we can reverse the UTXODiffs, to create shorter UTXODiffChild paths.
|
||||||
|
// However, we can't do this right now, because the tip of the chain is not yet committed, so we prepare the
|
||||||
|
// needed data (tip's selectedParent and selectedParent's UTXODiff)
|
||||||
|
selectedParentUTXODiff, err := previousBlockUTXOSet.DiffFrom(oneBeforeLastResolvedBlockUTXOSet)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reversalData = &model.UTXODiffReversalData{
|
||||||
|
SelectedParentHash: oneBeforeLastResolvedBlockHash,
|
||||||
|
SelectedParentUTXODiff: selectedParentUTXODiff,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return blockStatus, nil
|
return blockStatus, reversalData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// findSelectedParentStatus returns the status of the selectedParent of the last block in the unverifiedBlocks chain
|
// selectedParentInfo returns the hash and status of the selectedParent of the last block in the unverifiedBlocks
|
||||||
func (csm *consensusStateManager) findSelectedParentStatus(
|
// chain, in addition, if the status is UTXOValid, it return it's pastUTXOSet
|
||||||
stagingArea *model.StagingArea, unverifiedBlocks []*externalapi.DomainHash) (externalapi.BlockStatus, error) {
|
func (csm *consensusStateManager) selectedParentInfo(
|
||||||
|
stagingArea *model.StagingArea, unverifiedBlocks []*externalapi.DomainHash) (
|
||||||
|
*externalapi.DomainHash, externalapi.BlockStatus, externalapi.UTXODiff, error) {
|
||||||
|
|
||||||
log.Debugf("findSelectedParentStatus start")
|
log.Debugf("findSelectedParentStatus start")
|
||||||
defer log.Debugf("findSelectedParentStatus end")
|
defer log.Debugf("findSelectedParentStatus end")
|
||||||
@ -94,13 +129,26 @@ func (csm *consensusStateManager) findSelectedParentStatus(
|
|||||||
if lastUnverifiedBlock.Equal(csm.genesisHash) {
|
if lastUnverifiedBlock.Equal(csm.genesisHash) {
|
||||||
log.Debugf("the most recent unverified block is the genesis block, "+
|
log.Debugf("the most recent unverified block is the genesis block, "+
|
||||||
"which by definition has status: %s", externalapi.StatusUTXOValid)
|
"which by definition has status: %s", externalapi.StatusUTXOValid)
|
||||||
return externalapi.StatusUTXOValid, nil
|
return lastUnverifiedBlock, externalapi.StatusUTXOValid, utxo.NewUTXODiff(), nil
|
||||||
}
|
}
|
||||||
lastUnverifiedBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, lastUnverifiedBlock)
|
lastUnverifiedBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, lastUnverifiedBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, 0, nil, err
|
||||||
}
|
}
|
||||||
return csm.blockStatusStore.Get(csm.databaseContext, stagingArea, lastUnverifiedBlockGHOSTDAGData.SelectedParent())
|
selectedParent := lastUnverifiedBlockGHOSTDAGData.SelectedParent()
|
||||||
|
selectedParentStatus, err := csm.blockStatusStore.Get(csm.databaseContext, stagingArea, selectedParent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, nil, err
|
||||||
|
}
|
||||||
|
if selectedParentStatus != externalapi.StatusUTXOValid {
|
||||||
|
return selectedParent, selectedParentStatus, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedParentUTXOSet, err := csm.restorePastUTXO(stagingArea, selectedParent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, nil, err
|
||||||
|
}
|
||||||
|
return selectedParent, selectedParentStatus, selectedParentUTXOSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) getUnverifiedChainBlocks(stagingArea *model.StagingArea,
|
func (csm *consensusStateManager) getUnverifiedChainBlocks(stagingArea *model.StagingArea,
|
||||||
@ -142,15 +190,17 @@ func (csm *consensusStateManager) getUnverifiedChainBlocks(stagingArea *model.St
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) resolveSingleBlockStatus(stagingArea *model.StagingArea,
|
func (csm *consensusStateManager) resolveSingleBlockStatus(stagingArea *model.StagingArea,
|
||||||
blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error) {
|
blockHash, selectedParentHash *externalapi.DomainHash, selectedParentPastUTXOSet externalapi.UTXODiff, isResolveTip bool) (
|
||||||
|
externalapi.BlockStatus, externalapi.UTXODiff, error) {
|
||||||
|
|
||||||
onEnd := logger.LogAndMeasureExecutionTime(log, fmt.Sprintf("resolveSingleBlockStatus for %s", blockHash))
|
onEnd := logger.LogAndMeasureExecutionTime(log, fmt.Sprintf("resolveSingleBlockStatus for %s", blockHash))
|
||||||
defer onEnd()
|
defer onEnd()
|
||||||
|
|
||||||
log.Tracef("Calculating pastUTXO and acceptance data and multiset for block %s", blockHash)
|
log.Tracef("Calculating pastUTXO and acceptance data and multiset for block %s", blockHash)
|
||||||
pastUTXODiff, acceptanceData, multiset, err := csm.CalculatePastUTXOAndAcceptanceData(stagingArea, blockHash)
|
pastUTXOSet, acceptanceData, multiset, err := csm.calculatePastUTXOAndAcceptanceDataWithSelectedParentUTXO(
|
||||||
|
stagingArea, blockHash, selectedParentPastUTXOSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("Staging the calculated acceptance data of block %s", blockHash)
|
log.Tracef("Staging the calculated acceptance data of block %s", blockHash)
|
||||||
@ -158,17 +208,17 @@ func (csm *consensusStateManager) resolveSingleBlockStatus(stagingArea *model.St
|
|||||||
|
|
||||||
block, err := csm.blockStore.Block(csm.databaseContext, stagingArea, blockHash)
|
block, err := csm.blockStore.Block(csm.databaseContext, stagingArea, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("verifying the UTXO of block %s", blockHash)
|
log.Tracef("verifying the UTXO of block %s", blockHash)
|
||||||
err = csm.verifyUTXO(stagingArea, block, blockHash, pastUTXODiff, acceptanceData, multiset)
|
err = csm.verifyUTXO(stagingArea, block, blockHash, pastUTXOSet, acceptanceData, multiset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||||
log.Debugf("UTXO verification for block %s failed: %s", blockHash, err)
|
log.Debugf("UTXO verification for block %s failed: %s", blockHash, err)
|
||||||
return externalapi.StatusDisqualifiedFromChain, nil
|
return externalapi.StatusDisqualifiedFromChain, nil, nil
|
||||||
}
|
}
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("UTXO verification for block %s passed", blockHash)
|
log.Debugf("UTXO verification for block %s passed", blockHash)
|
||||||
|
|
||||||
@ -177,45 +227,62 @@ func (csm *consensusStateManager) resolveSingleBlockStatus(stagingArea *model.St
|
|||||||
|
|
||||||
if csm.genesisHash.Equal(blockHash) {
|
if csm.genesisHash.Equal(blockHash) {
|
||||||
log.Tracef("Staging the utxoDiff of genesis")
|
log.Tracef("Staging the utxoDiff of genesis")
|
||||||
csm.stageDiff(stagingArea, blockHash, pastUTXODiff, nil)
|
csm.stageDiff(stagingArea, blockHash, pastUTXOSet, nil)
|
||||||
return externalapi.StatusUTXOValid, nil
|
return externalapi.StatusUTXOValid, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
oldSelectedTip, err := csm.selectedTip(stagingArea)
|
oldSelectedTip, err := csm.selectedTip(stagingArea)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
isNewSelectedTip, err := csm.isNewSelectedTip(stagingArea, blockHash, oldSelectedTip)
|
if isResolveTip {
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
oldSelectedTipUTXOSet, err := csm.restorePastUTXO(stagingArea, oldSelectedTip)
|
oldSelectedTipUTXOSet, err := csm.restorePastUTXO(stagingArea, oldSelectedTip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
isNewSelectedTip, err := csm.isNewSelectedTip(stagingArea, blockHash, oldSelectedTip)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if isNewSelectedTip {
|
if isNewSelectedTip {
|
||||||
log.Debugf("Block %s is the new SelectedTip, therefore setting it as old selectedTip's diffChild", blockHash)
|
log.Debugf("Block %s is the new selected tip, therefore setting it as old selected tip's diffChild", blockHash)
|
||||||
oldSelectedTipUTXOSet, err := pastUTXODiff.DiffFrom(oldSelectedTipUTXOSet.ToImmutable())
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
csm.stageDiff(stagingArea, oldSelectedTip, oldSelectedTipUTXOSet, blockHash)
|
|
||||||
|
|
||||||
log.Tracef("Staging the utxoDiff of block %s", blockHash)
|
updatedOldSelectedTipUTXOSet, err := pastUTXOSet.DiffFrom(oldSelectedTipUTXOSet)
|
||||||
csm.stageDiff(stagingArea, blockHash, pastUTXODiff, nil)
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Setting the old selected tip's (%s) diffChild to be the new selected tip (%s)",
|
||||||
|
oldSelectedTip, blockHash)
|
||||||
|
csm.stageDiff(stagingArea, oldSelectedTip, updatedOldSelectedTipUTXOSet, blockHash)
|
||||||
|
|
||||||
|
log.Tracef("Staging the utxoDiff of block %s, with virtual as diffChild", blockHash)
|
||||||
|
csm.stageDiff(stagingArea, blockHash, pastUTXOSet, nil)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("Block %s is not the new SelectedTip, therefore setting old selectedTip as it's diffChild", blockHash)
|
log.Debugf("Block %s is the tip of currently resolved chain, but not the new selected tip,"+
|
||||||
pastUTXODiff, err = oldSelectedTipUTXOSet.DiffFrom(pastUTXODiff)
|
"therefore setting it's utxoDiffChild to be the current selectedTip %s", blockHash, oldSelectedTip)
|
||||||
|
utxoDiff, err := oldSelectedTipUTXOSet.DiffFrom(pastUTXOSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
csm.stageDiff(stagingArea, blockHash, utxoDiff, oldSelectedTip)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the block is not the tip of the currently resolved chain, we set it's diffChild to be the selectedParent,
|
||||||
|
// this is a temporary measure to ensure there's a restore path to all blocks at all times.
|
||||||
|
// Later down the process, the diff will be reversed in reverseUTXODiffs.
|
||||||
|
log.Debugf("Block %s is not the new selected tip, and is not the tip of the currently verified chain, "+
|
||||||
|
"therefore temporarily setting selectedParent as it's diffChild", blockHash)
|
||||||
|
utxoDiff, err := selectedParentPastUTXOSet.DiffFrom(pastUTXOSet)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("Staging the utxoDiff of block %s", blockHash)
|
csm.stageDiff(stagingArea, blockHash, utxoDiff, selectedParentHash)
|
||||||
csm.stageDiff(stagingArea, blockHash, pastUTXODiff, oldSelectedTip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return externalapi.StatusUTXOValid, nil
|
return externalapi.StatusUTXOValid, pastUTXOSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) isNewSelectedTip(stagingArea *model.StagingArea,
|
func (csm *consensusStateManager) isNewSelectedTip(stagingArea *model.StagingArea,
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
package consensusstatemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
|
"github.com/kaspanet/kaspad/util/staging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (csm *consensusStateManager) ReverseUTXODiffs(tipHash *externalapi.DomainHash,
|
||||||
|
reversalData *model.UTXODiffReversalData) error {
|
||||||
|
|
||||||
|
// During the process of resolving a chain of blocks, we temporarily set all blocks' (except the tip)
|
||||||
|
// UTXODiffChild to be the selected parent.
|
||||||
|
// Once the process is complete, we can reverse said chain, to now go directly to virtual through the relevant tip
|
||||||
|
onEnd := logger.LogAndMeasureExecutionTime(log, "reverseUTXODiffs")
|
||||||
|
defer onEnd()
|
||||||
|
|
||||||
|
readStagingArea := model.NewStagingArea()
|
||||||
|
|
||||||
|
log.Debugf("Reversing utxoDiffs")
|
||||||
|
|
||||||
|
// Set previousUTXODiff and previousBlock to tip.SelectedParent before we start touching them,
|
||||||
|
// since previousBlock's UTXODiff is going to be over-written in the next step
|
||||||
|
previousBlock := reversalData.SelectedParentHash
|
||||||
|
previousUTXODiff, err := csm.utxoDiffStore.UTXODiff(csm.databaseContext, readStagingArea, previousBlock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// tip.selectedParent is special in the sense that we don't have it's diff available in reverse, however,
|
||||||
|
// we were able to calculate it when the tip's and tip.selectedParent's UTXOSets were known during resolveBlockStatus.
|
||||||
|
// Therefore - we treat it separately
|
||||||
|
err = csm.commitUTXODiffInSeparateStagingArea(previousBlock, reversalData.SelectedParentUTXODiff, tipHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("Reversed 1 utxoDiff")
|
||||||
|
|
||||||
|
previousBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, readStagingArea, previousBlock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Now go over the rest of the blocks and assign for every block Bi.UTXODiff = Bi+1.UTXODiff.Reversed()
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
currentBlock := previousBlockGHOSTDAGData.SelectedParent()
|
||||||
|
|
||||||
|
currentBlockUTXODiffChild, err := csm.utxoDiffStore.UTXODiffChild(csm.databaseContext, readStagingArea, currentBlock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
currentBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, readStagingArea, currentBlock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We stop reversing when current's UTXODiffChild is not current's SelectedParent
|
||||||
|
if !currentBlockGHOSTDAGData.SelectedParent().Equal(currentBlockUTXODiffChild) {
|
||||||
|
log.Debugf("Block %s's UTXODiffChild is not it's selected parent - finish reversing", currentBlock)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
currentUTXODiff := previousUTXODiff.Reversed()
|
||||||
|
|
||||||
|
// retrieve current utxoDiff for Bi, to be used by next block
|
||||||
|
previousUTXODiff, err = csm.utxoDiffStore.UTXODiff(csm.databaseContext, readStagingArea, currentBlock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = csm.commitUTXODiffInSeparateStagingArea(currentBlock, currentUTXODiff, previousBlock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
previousBlock = currentBlock
|
||||||
|
previousBlockGHOSTDAGData = currentBlockGHOSTDAGData
|
||||||
|
|
||||||
|
log.Tracef("Reversed %d utxoDiffs", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csm *consensusStateManager) commitUTXODiffInSeparateStagingArea(
|
||||||
|
blockHash *externalapi.DomainHash, utxoDiff externalapi.UTXODiff, utxoDiffChild *externalapi.DomainHash) error {
|
||||||
|
|
||||||
|
stagingAreaForCurrentBlock := model.NewStagingArea()
|
||||||
|
|
||||||
|
csm.utxoDiffStore.Stage(stagingAreaForCurrentBlock, blockHash, utxoDiff, utxoDiffChild)
|
||||||
|
|
||||||
|
return staging.CommitAllChanges(csm.databaseContext, stagingAreaForCurrentBlock)
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
package consensusstatemanager_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReverseUTXODiffs(t *testing.T) {
|
||||||
|
// This test doesn't check ReverseUTXODiffs directly, since that would be quite complicated,
|
||||||
|
// instead, it creates a situation where a reversal would defenitely happen - a reorg of 5 blocks,
|
||||||
|
// then verifies that the resulting utxo-diffs and utxo-diff-children are all correct.
|
||||||
|
|
||||||
|
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||||
|
factory := consensus.NewFactory()
|
||||||
|
|
||||||
|
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestUTXOCommitment")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error setting up consensus: %+v", err)
|
||||||
|
}
|
||||||
|
defer teardown(false)
|
||||||
|
|
||||||
|
// Create a chain of 5 blocks
|
||||||
|
const initialChainLength = 5
|
||||||
|
previousBlockHash := consensusConfig.GenesisHash
|
||||||
|
for i := 0; i < initialChainLength; i++ {
|
||||||
|
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mine a chain of 6 blocks, to re-organize the DAG
|
||||||
|
const reorgChainLength = initialChainLength + 1
|
||||||
|
reorgChain := make([]*externalapi.DomainHash, reorgChainLength)
|
||||||
|
previousBlockHash = consensusConfig.GenesisHash
|
||||||
|
for i := 0; i < reorgChainLength; i++ {
|
||||||
|
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||||
|
reorgChain[i] = previousBlockHash
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stagingArea := model.NewStagingArea()
|
||||||
|
// Check that every block in the reorg chain has the next block as it's UTXODiffChild,
|
||||||
|
// except that tip that has virtual, And that the diff is only `{ toRemove: { coinbase } }`
|
||||||
|
for i, currentBlockHash := range reorgChain {
|
||||||
|
if i == reorgChainLength-1 {
|
||||||
|
hasUTXODiffChild, err := tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, currentBlockHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting HasUTXODiffChild of %s: %+v", currentBlockHash, err)
|
||||||
|
}
|
||||||
|
if hasUTXODiffChild {
|
||||||
|
t.Errorf("Block %s expected utxoDiffChild is virtual, but HasUTXODiffChild returned true",
|
||||||
|
currentBlockHash)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
utxoDiffChild, err := tc.UTXODiffStore().UTXODiffChild(tc.DatabaseContext(), stagingArea, currentBlockHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting utxoDiffChild of block No. %d, %s: %+v", i, currentBlockHash, err)
|
||||||
|
}
|
||||||
|
expectedUTXODiffChild := reorgChain[i+1]
|
||||||
|
if !expectedUTXODiffChild.Equal(utxoDiffChild) {
|
||||||
|
t.Errorf("Block %s expected utxoDiffChild is %s, but got %s instead",
|
||||||
|
currentBlockHash, expectedUTXODiffChild, utxoDiffChild)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip the first block, since it's coinbase doesn't create outputs
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBlock, err := tc.BlockStore().Block(tc.DatabaseContext(), stagingArea, currentBlockHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting block %s: %+v", currentBlockHash, err)
|
||||||
|
}
|
||||||
|
utxoDiff, err := tc.UTXODiffStore().UTXODiff(tc.DatabaseContext(), stagingArea, currentBlockHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting utxoDiffChild of %s: %+v", currentBlockHash, err)
|
||||||
|
}
|
||||||
|
if !checkIsUTXODiffOnlyRemoveCoinbase(t, utxoDiff, currentBlock) {
|
||||||
|
t.Errorf("Expected %s to only have toRemove: {%s}, but got %s instead",
|
||||||
|
currentBlockHash, consensushashing.TransactionID(currentBlock.Transactions[0]), utxoDiff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIsUTXODiffOnlyRemoveCoinbase(t *testing.T, utxoDiff externalapi.UTXODiff, currentBlock *externalapi.DomainBlock) bool {
|
||||||
|
if utxoDiff.ToAdd().Len() > 0 || utxoDiff.ToRemove().Len() > 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator := utxoDiff.ToRemove().Iterator()
|
||||||
|
iterator.First()
|
||||||
|
outpoint, _, err := iterator.Get()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting from UTXODiff's iterator: %+v", err)
|
||||||
|
}
|
||||||
|
if !outpoint.TransactionID.Equal(consensushashing.TransactionID(currentBlock.Transactions[0])) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
@ -22,7 +22,8 @@ func (csm *testConsensusStateManager) AddUTXOToMultiset(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (csm *testConsensusStateManager) ResolveBlockStatus(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
func (csm *testConsensusStateManager) ResolveBlockStatus(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||||
useSeparateStagingAreasPerBlock bool) (externalapi.BlockStatus, error) {
|
useSeparateStagingAreaPerBlock bool) (externalapi.BlockStatus, error) {
|
||||||
|
|
||||||
return csm.resolveBlockStatus(stagingArea, blockHash, useSeparateStagingAreasPerBlock)
|
status, _, err := csm.resolveBlockStatus(stagingArea, blockHash, useSeparateStagingAreaPerBlock)
|
||||||
|
return status, err
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
|
|||||||
for j := 0; j <= i; j++ {
|
for j := 0; j <= i; j++ {
|
||||||
lastBlock, _, err = tc.AddBlock([]*externalapi.DomainHash{lastBlock}, nil, nil)
|
lastBlock, _, err = tc.AddBlock([]*externalapi.DomainHash{lastBlock}, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed Adding block to tc: %v", err)
|
t.Fatalf("Failed Adding block to tc: %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parents = append(parents, lastBlock)
|
parents = append(parents, lastBlock)
|
||||||
@ -98,7 +98,7 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
|
|||||||
for i := 0; i < int(consensusConfig.MaxBlockParents); i++ {
|
for i := 0; i < int(consensusConfig.MaxBlockParents); i++ {
|
||||||
block, _, err := tc.AddBlock([]*externalapi.DomainHash{virtualSelectedParent}, nil, nil)
|
block, _, err := tc.AddBlock([]*externalapi.DomainHash{virtualSelectedParent}, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed Adding block to tc: %v", err)
|
t.Fatalf("Failed Adding block to tc: %+v", err)
|
||||||
}
|
}
|
||||||
parents = append(parents, block)
|
parents = append(parents, block)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RenderDAGToDot is a helper function for debugging tests.
|
// RenderDAGToDot is a helper function for debugging tests.
|
||||||
@ -28,6 +30,7 @@ func (tc *testConsensus) convertToDot() (string, error) {
|
|||||||
}
|
}
|
||||||
defer blocksIterator.Close()
|
defer blocksIterator.Close()
|
||||||
|
|
||||||
|
stagingArea := model.NewStagingArea()
|
||||||
for ok := blocksIterator.First(); ok; ok = blocksIterator.Next() {
|
for ok := blocksIterator.First(); ok; ok = blocksIterator.Next() {
|
||||||
hash, err := blocksIterator.Get()
|
hash, err := blocksIterator.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -35,7 +38,7 @@ func (tc *testConsensus) convertToDot() (string, error) {
|
|||||||
}
|
}
|
||||||
dotScriptBuilder.WriteString(fmt.Sprintf("\t\"%s\";\n", hash))
|
dotScriptBuilder.WriteString(fmt.Sprintf("\t\"%s\";\n", hash))
|
||||||
|
|
||||||
parents, err := tc.dagTopologyManager.Parents(nil, hash)
|
parents, err := tc.dagTopologyManager.Parents(stagingArea, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -34,23 +34,6 @@ func checkIntersectionWithRule(collection1 utxoCollection, collection2 utxoColle
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// minInt returns the smaller of x or y integer values
|
|
||||||
func minInt(a, b int) int {
|
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// intersectionWithRemainderHavingDAAScore calculates an intersection between two utxoCollections
|
|
||||||
// having same DAA score, returns the result and the remainder from collection1
|
|
||||||
func intersectionWithRemainderHavingDAAScore(collection1, collection2 utxoCollection) (result, remainder utxoCollection) {
|
|
||||||
result = make(utxoCollection, minInt(len(collection1), len(collection2)))
|
|
||||||
remainder = make(utxoCollection, len(collection1))
|
|
||||||
intersectionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// intersectionWithRemainderHavingDAAScoreInPlace calculates an intersection between two utxoCollections
|
// intersectionWithRemainderHavingDAAScoreInPlace calculates an intersection between two utxoCollections
|
||||||
// having same DAA score, puts it into result and into remainder from collection1
|
// having same DAA score, puts it into result and into remainder from collection1
|
||||||
func intersectionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
|
func intersectionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
|
||||||
@ -63,15 +46,6 @@ func intersectionWithRemainderHavingDAAScoreInPlace(collection1, collection2, re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// subtractionHavingDAAScore calculates a subtraction between collection1 and collection2
|
|
||||||
// having same DAA score, returns the result
|
|
||||||
func subtractionHavingDAAScore(collection1, collection2 utxoCollection) (result utxoCollection) {
|
|
||||||
result = make(utxoCollection, len(collection1))
|
|
||||||
|
|
||||||
subtractionHavingDAAScoreInPlace(collection1, collection2, result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtractionHavingDAAScoreInPlace calculates a subtraction between collection1 and collection2
|
// subtractionHavingDAAScoreInPlace calculates a subtraction between collection1 and collection2
|
||||||
// having same DAA score, puts it into result
|
// having same DAA score, puts it into result
|
||||||
func subtractionHavingDAAScoreInPlace(collection1, collection2, result utxoCollection) {
|
func subtractionHavingDAAScoreInPlace(collection1, collection2, result utxoCollection) {
|
||||||
@ -82,16 +56,6 @@ func subtractionHavingDAAScoreInPlace(collection1, collection2, result utxoColle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// subtractionWithRemainderHavingDAAScore calculates a subtraction between collection1 and collection2
|
|
||||||
// having same DAA score, returns the result and the remainder from collection1
|
|
||||||
func subtractionWithRemainderHavingDAAScore(collection1, collection2 utxoCollection) (result, remainder utxoCollection) {
|
|
||||||
result = make(utxoCollection, len(collection1))
|
|
||||||
remainder = make(utxoCollection, len(collection1))
|
|
||||||
|
|
||||||
subtractionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtractionWithRemainderHavingDAAScoreInPlace calculates a subtraction between collection1 and collection2
|
// subtractionWithRemainderHavingDAAScoreInPlace calculates a subtraction between collection1 and collection2
|
||||||
// having same DAA score, puts it into result and into remainder from collection1
|
// having same DAA score, puts it into result and into remainder from collection1
|
||||||
func subtractionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
|
func subtractionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
|
||||||
@ -175,13 +139,13 @@ func diffFrom(this, other *mutableUTXODiff) (*mutableUTXODiff, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &mutableUTXODiff{
|
result := &mutableUTXODiff{
|
||||||
toAdd: make(utxoCollection, len(this.toRemove)+len(other.toAdd)),
|
toAdd: make(utxoCollection),
|
||||||
toRemove: make(utxoCollection, len(this.toAdd)+len(other.toRemove)),
|
toRemove: make(utxoCollection),
|
||||||
}
|
}
|
||||||
|
|
||||||
// All transactions in this.toAdd:
|
// All transactions in this.toAdd:
|
||||||
// If they are not in other.toAdd - should be added in result.toRemove
|
// If they are not in other.toAdd - should be added in result.toRemove
|
||||||
inBothToAdd := make(utxoCollection, len(this.toAdd))
|
inBothToAdd := make(utxoCollection)
|
||||||
subtractionWithRemainderHavingDAAScoreInPlace(this.toAdd, other.toAdd, result.toRemove, inBothToAdd)
|
subtractionWithRemainderHavingDAAScoreInPlace(this.toAdd, other.toAdd, result.toRemove, inBothToAdd)
|
||||||
// If they are in other.toRemove - base utxoSet is not the same
|
// If they are in other.toRemove - base utxoSet is not the same
|
||||||
if checkIntersection(inBothToAdd, this.toRemove) != checkIntersection(inBothToAdd, other.toRemove) {
|
if checkIntersection(inBothToAdd, this.toRemove) != checkIntersection(inBothToAdd, other.toRemove) {
|
||||||
@ -223,13 +187,13 @@ func withDiffInPlace(this *mutableUTXODiff, other *mutableUTXODiff) error {
|
|||||||
"withDiffInPlace: outpoint %s both in this.toAdd and in other.toAdd", offendingOutpoint)
|
"withDiffInPlace: outpoint %s both in this.toAdd and in other.toAdd", offendingOutpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
intersection := make(utxoCollection, minInt(len(other.toRemove), len(this.toAdd)))
|
intersection := make(utxoCollection)
|
||||||
// If not exists neither in toAdd nor in toRemove - add to toRemove
|
// If not exists neither in toAdd nor in toRemove - add to toRemove
|
||||||
intersectionWithRemainderHavingDAAScoreInPlace(other.toRemove, this.toAdd, intersection, this.toRemove)
|
intersectionWithRemainderHavingDAAScoreInPlace(other.toRemove, this.toAdd, intersection, this.toRemove)
|
||||||
// If already exists in toAdd with the same DAA score - remove from toAdd
|
// If already exists in toAdd with the same DAA score - remove from toAdd
|
||||||
this.toAdd.removeMultiple(intersection)
|
this.toAdd.removeMultiple(intersection)
|
||||||
|
|
||||||
intersection = make(utxoCollection, minInt(len(other.toAdd), len(this.toRemove)))
|
intersection = make(utxoCollection)
|
||||||
// If not exists neither in toAdd nor in toRemove, or exists in toRemove with different DAA score - add to toAdd
|
// If not exists neither in toAdd nor in toRemove, or exists in toRemove with different DAA score - add to toAdd
|
||||||
intersectionWithRemainderHavingDAAScoreInPlace(other.toAdd, this.toRemove, intersection, this.toAdd)
|
intersectionWithRemainderHavingDAAScoreInPlace(other.toAdd, this.toRemove, intersection, this.toAdd)
|
||||||
// If already exists in toRemove with the same DAA score - remove from toRemove
|
// If already exists in toRemove with the same DAA score - remove from toRemove
|
||||||
|
@ -13,7 +13,7 @@ type immutableUTXODiff struct {
|
|||||||
|
|
||||||
func (iud *immutableUTXODiff) ToAdd() externalapi.UTXOCollection {
|
func (iud *immutableUTXODiff) ToAdd() externalapi.UTXOCollection {
|
||||||
if iud.isInvalidated {
|
if iud.isInvalidated {
|
||||||
panic("Attempt to read from an invalidated UTXODiff")
|
panic(errors.New("Attempt to read from an invalidated UTXODiff"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return iud.mutableUTXODiff.ToAdd()
|
return iud.mutableUTXODiff.ToAdd()
|
||||||
@ -21,7 +21,7 @@ func (iud *immutableUTXODiff) ToAdd() externalapi.UTXOCollection {
|
|||||||
|
|
||||||
func (iud *immutableUTXODiff) ToRemove() externalapi.UTXOCollection {
|
func (iud *immutableUTXODiff) ToRemove() externalapi.UTXOCollection {
|
||||||
if iud.isInvalidated {
|
if iud.isInvalidated {
|
||||||
panic("Attempt to read from an invalidated UTXODiff")
|
panic(errors.New("Attempt to read from an invalidated UTXODiff"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return iud.mutableUTXODiff.ToRemove()
|
return iud.mutableUTXODiff.ToRemove()
|
||||||
@ -29,7 +29,7 @@ func (iud *immutableUTXODiff) ToRemove() externalapi.UTXOCollection {
|
|||||||
|
|
||||||
func (iud *immutableUTXODiff) WithDiff(other externalapi.UTXODiff) (externalapi.UTXODiff, error) {
|
func (iud *immutableUTXODiff) WithDiff(other externalapi.UTXODiff) (externalapi.UTXODiff, error) {
|
||||||
if iud.isInvalidated {
|
if iud.isInvalidated {
|
||||||
panic("Attempt to read from an invalidated UTXODiff")
|
panic(errors.New("Attempt to read from an invalidated UTXODiff"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return iud.mutableUTXODiff.WithDiff(other)
|
return iud.mutableUTXODiff.WithDiff(other)
|
||||||
@ -37,7 +37,7 @@ func (iud *immutableUTXODiff) WithDiff(other externalapi.UTXODiff) (externalapi.
|
|||||||
|
|
||||||
func (iud *immutableUTXODiff) DiffFrom(other externalapi.UTXODiff) (externalapi.UTXODiff, error) {
|
func (iud *immutableUTXODiff) DiffFrom(other externalapi.UTXODiff) (externalapi.UTXODiff, error) {
|
||||||
if iud.isInvalidated {
|
if iud.isInvalidated {
|
||||||
panic("Attempt to read from an invalidated UTXODiff")
|
panic(errors.New("Attempt to read from an invalidated UTXODiff"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return iud.mutableUTXODiff.DiffFrom(other)
|
return iud.mutableUTXODiff.DiffFrom(other)
|
||||||
@ -74,9 +74,22 @@ func NewUTXODiffFromCollections(toAdd, toRemove externalapi.UTXOCollection) (ext
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (iud *immutableUTXODiff) CloneMutable() externalapi.MutableUTXODiff {
|
func (iud *immutableUTXODiff) CloneMutable() externalapi.MutableUTXODiff {
|
||||||
|
if iud.isInvalidated {
|
||||||
|
panic(errors.New("Attempt to read from an invalidated UTXODiff"))
|
||||||
|
}
|
||||||
return iud.cloneMutable()
|
return iud.cloneMutable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (iud *immutableUTXODiff) Reversed() externalapi.UTXODiff {
|
||||||
|
if iud.isInvalidated {
|
||||||
|
panic(errors.New("Attempt to read from an invalidated UTXODiff"))
|
||||||
|
}
|
||||||
|
return &immutableUTXODiff{
|
||||||
|
mutableUTXODiff: iud.mutableUTXODiff.Reversed(),
|
||||||
|
isInvalidated: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (iud *immutableUTXODiff) cloneMutable() *mutableUTXODiff {
|
func (iud *immutableUTXODiff) cloneMutable() *mutableUTXODiff {
|
||||||
if iud == nil {
|
if iud == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -84,3 +97,7 @@ func (iud *immutableUTXODiff) cloneMutable() *mutableUTXODiff {
|
|||||||
|
|
||||||
return iud.mutableUTXODiff.clone()
|
return iud.mutableUTXODiff.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (iud immutableUTXODiff) String() string {
|
||||||
|
return iud.mutableUTXODiff.String()
|
||||||
|
}
|
||||||
|
@ -157,3 +157,11 @@ func (mud *mutableUTXODiff) clone() *mutableUTXODiff {
|
|||||||
func (mud *mutableUTXODiff) String() string {
|
func (mud *mutableUTXODiff) String() string {
|
||||||
return fmt.Sprintf("toAdd: %s; toRemove: %s", mud.toAdd, mud.toRemove)
|
return fmt.Sprintf("toAdd: %s; toRemove: %s", mud.toAdd, mud.toRemove)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mud *mutableUTXODiff) Reversed() *mutableUTXODiff {
|
||||||
|
return &mutableUTXODiff{
|
||||||
|
toAdd: mud.toRemove,
|
||||||
|
toRemove: mud.toAdd,
|
||||||
|
immutableReferences: mud.immutableReferences,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Binary file not shown.
@ -19,6 +19,7 @@ import (
|
|||||||
func testReorg(cfg *configFlags) {
|
func testReorg(cfg *configFlags) {
|
||||||
consensusConfig := consensus.Config{Params: dagconfig.DevnetParams}
|
consensusConfig := consensus.Config{Params: dagconfig.DevnetParams}
|
||||||
consensusConfig.SkipProofOfWork = true
|
consensusConfig.SkipProofOfWork = true
|
||||||
|
consensusConfig.DisableDifficultyAdjustment = true
|
||||||
|
|
||||||
factory := consensus.NewFactory()
|
factory := consensus.NewFactory()
|
||||||
tc, teardown, err := factory.NewTestConsensus(&consensusConfig, "ReorgHonest")
|
tc, teardown, err := factory.NewTestConsensus(&consensusConfig, "ReorgHonest")
|
||||||
@ -130,7 +131,7 @@ func testReorg(cfg *configFlags) {
|
|||||||
doneChan <- struct{}{}
|
doneChan <- struct{}{}
|
||||||
})
|
})
|
||||||
|
|
||||||
const timeout = 10 * time.Minute
|
const timeout = 12 * time.Hour
|
||||||
select {
|
select {
|
||||||
case <-doneChan:
|
case <-doneChan:
|
||||||
case <-time.After(timeout):
|
case <-time.After(timeout):
|
||||||
|
12
stability-tests/reorg/run/run-full-finality-window-reorg.sh
Executable file
12
stability-tests/reorg/run/run-full-finality-window-reorg.sh
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
reorg --dag-file ../../netsync/dags-slow/wide-dag-blocks--2^16-delay-factor--1-k--18.json.gz --profile=6061
|
||||||
|
|
||||||
|
TEST_EXIT_CODE=$?
|
||||||
|
echo "Exit code: $TEST_EXIT_CODE"
|
||||||
|
|
||||||
|
|
||||||
|
if [ $TEST_EXIT_CODE -eq 0 ]; then
|
||||||
|
echo "reorg test: PASSED"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "reorg test: FAILED"
|
||||||
|
exit 1
|
@ -35,7 +35,7 @@ cd "${PROJECT_ROOT}/orphans/run" && ./run.sh || failedTests+=("orphans")
|
|||||||
echo "Done running orphans"
|
echo "Done running orphans"
|
||||||
|
|
||||||
echo "Running reorg"
|
echo "Running reorg"
|
||||||
cd "${PROJECT_ROOT}/reorg/run" && ./run.sh || failedTests+=("reorg")
|
cd "${PROJECT_ROOT}/reorg/run" && ./run-full-finality-window-reorg.sh || failedTests+=("reorg")
|
||||||
echo "Done running reorg"
|
echo "Done running reorg"
|
||||||
|
|
||||||
echo "Running mempool-limits"
|
echo "Running mempool-limits"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user