mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-05 21:56:50 +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
|
||||
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.utxoDiffToAdd[*blockHash] = utxoDiff
|
||||
|
@ -67,7 +67,7 @@ func TestFinality(t *testing.T) {
|
||||
for i := uint64(0); i < finalityInterval-2; i++ {
|
||||
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
||||
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)
|
||||
|
||||
|
@ -14,6 +14,7 @@ type UTXODiff interface {
|
||||
ToRemove() UTXOCollection
|
||||
WithDiff(other UTXODiff) (UTXODiff, error)
|
||||
DiffFrom(other UTXODiff) (UTXODiff, error)
|
||||
Reversed() UTXODiff
|
||||
CloneMutable() MutableUTXODiff
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,12 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// ConsensusStateManager manages the node's consensus state
|
||||
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
|
||||
ImportPruningPoint(stagingArea *StagingArea, newPruningPoint *externalapi.DomainBlock) error
|
||||
RestorePastUTXOSetIterator(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (externalapi.ReadOnlyUTXOSetIterator, error)
|
||||
CalculatePastUTXOAndAcceptanceData(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (externalapi.UTXODiff, externalapi.AcceptanceData, Multiset, error)
|
||||
GetVirtualSelectedParentChainFromBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, error)
|
||||
RecoverUTXOIfRequired() error
|
||||
ReverseUTXODiffs(tipHash *externalapi.DomainHash, reversalData *UTXODiffReversalData) error
|
||||
}
|
||||
|
@ -11,5 +11,5 @@ type TestConsensusStateManager interface {
|
||||
AddUTXOToMultiset(multiset model.Multiset, entry externalapi.UTXOEntry,
|
||||
outpoint *externalapi.DomainOutpoint) error
|
||||
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 virtualUTXODiff externalapi.UTXODiff
|
||||
var reversalData *model.UTXODiffReversalData
|
||||
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
|
||||
if !isHeaderOnlyBlock {
|
||||
// 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
|
||||
if !isPruningPoint {
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -124,6 +125,13 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if reversalData != nil {
|
||||
err = bp.consensusStateManager.ReverseUTXODiffs(blockHash, reversalData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = bp.pruningManager.UpdatePruningPointIfRequired()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
// current virtual. This process may result in a new virtual block
|
||||
// getting created
|
||||
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")
|
||||
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)
|
||||
isCandidateToBeNextVirtualSelectedParent, err := csm.isCandidateToBeNextVirtualSelectedParent(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var reversalData *model.UTXODiffReversalData
|
||||
if isCandidateToBeNextVirtualSelectedParent {
|
||||
// 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
|
||||
@ -29,7 +30,7 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
||||
"finality", blockHash)
|
||||
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if shouldNotify {
|
||||
@ -39,9 +40,10 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
||||
|
||||
if !isViolatingFinality {
|
||||
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 {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
newTips, err := csm.addTip(stagingArea, blockHash)
|
||||
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("Updating the virtual with the new tips")
|
||||
selectedParentChainChanges, virtualUTXODiff, err := csm.updateVirtual(stagingArea, blockHash, newTips)
|
||||
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(
|
||||
|
@ -39,14 +39,26 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(stagingArea
|
||||
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 {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
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())
|
||||
daaScore, err := csm.daaBlocksStore.DAAScore(csm.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Applying blue blocks to the selected parent past UTXO of block %s", blockHash)
|
||||
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(
|
||||
@ -66,7 +78,7 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(stagingArea
|
||||
}
|
||||
|
||||
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")
|
||||
defer onEnd()
|
||||
@ -120,11 +132,11 @@ func (csm *consensusStateManager) restorePastUTXO(
|
||||
}
|
||||
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,
|
||||
selectedParentPastUTXODiff externalapi.MutableUTXODiff, ghostdagData *model.BlockGHOSTDAGData, daaScore uint64) (
|
||||
selectedParentPastUTXODiff externalapi.UTXODiff, ghostdagData *model.BlockGHOSTDAGData, daaScore uint64) (
|
||||
externalapi.AcceptanceData, externalapi.MutableUTXODiff, error) {
|
||||
|
||||
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)
|
||||
|
||||
multiblockAcceptanceData := make(externalapi.AcceptanceData, len(mergeSetBlocks))
|
||||
accumulatedUTXODiff := selectedParentPastUTXODiff
|
||||
accumulatedUTXODiff := selectedParentPastUTXODiff.CloneMutable()
|
||||
accumulatedMass := uint64(0)
|
||||
|
||||
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
|
||||
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")
|
||||
defer onEnd()
|
||||
|
||||
blockStatus, err := csm.resolveBlockStatus(stagingArea, blockHash, true)
|
||||
blockStatus, _, err := csm.resolveBlockStatus(stagingArea, blockHash, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -306,5 +319,5 @@ func (csm *consensusStateManager) RestorePastUTXOSetIterator(stagingArea *model.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return utxo.IteratorWithDiff(virtualUTXOSetIterator, blockDiff.ToImmutable())
|
||||
return utxo.IteratorWithDiff(virtualUTXOSetIterator, blockDiff)
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package consensusstatemanager
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
@ -13,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
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))
|
||||
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)
|
||||
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, nil, err
|
||||
}
|
||||
log.Debugf("Got %d unverified blocks in the selected parent "+
|
||||
"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)
|
||||
status, err := csm.blockStatusStore.Get(csm.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, nil, err
|
||||
}
|
||||
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)
|
||||
selectedParentStatus, err := csm.findSelectedParentStatus(stagingArea, unverifiedBlocks)
|
||||
selectedParentHash, selectedParentStatus, selectedParentUTXOSet, err := csm.selectedParentInfo(stagingArea, unverifiedBlocks)
|
||||
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("Resolving the unverified blocks' status in reverse order (past to present)")
|
||||
var blockStatus externalapi.BlockStatus
|
||||
|
||||
previousBlockHash := selectedParentHash
|
||||
previousBlockUTXOSet := selectedParentUTXOSet
|
||||
var oneBeforeLastResolvedBlockUTXOSet externalapi.UTXODiff
|
||||
var oneBeforeLastResolvedBlockHash *externalapi.DomainHash
|
||||
|
||||
for i := len(unverifiedBlocks) - 1; i >= 0; i-- {
|
||||
unverifiedBlockHash := unverifiedBlocks[i]
|
||||
|
||||
stagingAreaForCurrentBlock := stagingArea
|
||||
useSeparateStagingArea := useSeparateStagingAreasPerBlock && (i != 0)
|
||||
isResolveTip := i == 0
|
||||
useSeparateStagingArea := useSeparateStagingAreaPerBlock && !isResolveTip
|
||||
if useSeparateStagingArea {
|
||||
stagingAreaForCurrentBlock = model.NewStagingArea()
|
||||
}
|
||||
@ -61,9 +70,13 @@ func (csm *consensusStateManager) resolveBlockStatus(stagingArea *model.StagingA
|
||||
if selectedParentStatus == externalapi.StatusDisqualifiedFromChain {
|
||||
blockStatus = externalapi.StatusDisqualifiedFromChain
|
||||
} else {
|
||||
blockStatus, err = csm.resolveSingleBlockStatus(stagingAreaForCurrentBlock, unverifiedBlockHash)
|
||||
oneBeforeLastResolvedBlockUTXOSet = previousBlockUTXOSet
|
||||
oneBeforeLastResolvedBlockHash = previousBlockHash
|
||||
|
||||
blockStatus, previousBlockUTXOSet, err = csm.resolveSingleBlockStatus(
|
||||
stagingAreaForCurrentBlock, unverifiedBlockHash, previousBlockHash, previousBlockUTXOSet, isResolveTip)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,17 +88,39 @@ func (csm *consensusStateManager) resolveBlockStatus(stagingArea *model.StagingA
|
||||
if useSeparateStagingArea {
|
||||
err := staging.CommitAllChanges(csm.databaseContext, stagingAreaForCurrentBlock)
|
||||
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
|
||||
func (csm *consensusStateManager) findSelectedParentStatus(
|
||||
stagingArea *model.StagingArea, unverifiedBlocks []*externalapi.DomainHash) (externalapi.BlockStatus, error) {
|
||||
// selectedParentInfo returns the hash and status of the selectedParent of the last block in the unverifiedBlocks
|
||||
// chain, in addition, if the status is UTXOValid, it return it's pastUTXOSet
|
||||
func (csm *consensusStateManager) selectedParentInfo(
|
||||
stagingArea *model.StagingArea, unverifiedBlocks []*externalapi.DomainHash) (
|
||||
*externalapi.DomainHash, externalapi.BlockStatus, externalapi.UTXODiff, error) {
|
||||
|
||||
log.Debugf("findSelectedParentStatus start")
|
||||
defer log.Debugf("findSelectedParentStatus end")
|
||||
@ -94,13 +129,26 @@ func (csm *consensusStateManager) findSelectedParentStatus(
|
||||
if lastUnverifiedBlock.Equal(csm.genesisHash) {
|
||||
log.Debugf("the most recent unverified block is the genesis block, "+
|
||||
"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)
|
||||
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,
|
||||
@ -142,15 +190,17 @@ func (csm *consensusStateManager) getUnverifiedChainBlocks(stagingArea *model.St
|
||||
}
|
||||
|
||||
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))
|
||||
defer onEnd()
|
||||
|
||||
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 {
|
||||
return 0, err
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
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 errors.As(err, &ruleerrors.RuleError{}) {
|
||||
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)
|
||||
|
||||
@ -177,45 +227,62 @@ func (csm *consensusStateManager) resolveSingleBlockStatus(stagingArea *model.St
|
||||
|
||||
if csm.genesisHash.Equal(blockHash) {
|
||||
log.Tracef("Staging the utxoDiff of genesis")
|
||||
csm.stageDiff(stagingArea, blockHash, pastUTXODiff, nil)
|
||||
return externalapi.StatusUTXOValid, nil
|
||||
csm.stageDiff(stagingArea, blockHash, pastUTXOSet, nil)
|
||||
return externalapi.StatusUTXOValid, nil, nil
|
||||
}
|
||||
|
||||
oldSelectedTip, err := csm.selectedTip(stagingArea)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
isNewSelectedTip, err := csm.isNewSelectedTip(stagingArea, blockHash, oldSelectedTip)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
oldSelectedTipUTXOSet, err := csm.restorePastUTXO(stagingArea, oldSelectedTip)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if isNewSelectedTip {
|
||||
log.Debugf("Block %s is the new SelectedTip, therefore setting it as old selectedTip's diffChild", blockHash)
|
||||
oldSelectedTipUTXOSet, err := pastUTXODiff.DiffFrom(oldSelectedTipUTXOSet.ToImmutable())
|
||||
if isResolveTip {
|
||||
oldSelectedTipUTXOSet, err := csm.restorePastUTXO(stagingArea, oldSelectedTip)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, nil, err
|
||||
}
|
||||
isNewSelectedTip, err := csm.isNewSelectedTip(stagingArea, blockHash, oldSelectedTip)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
csm.stageDiff(stagingArea, oldSelectedTip, oldSelectedTipUTXOSet, blockHash)
|
||||
|
||||
log.Tracef("Staging the utxoDiff of block %s", blockHash)
|
||||
csm.stageDiff(stagingArea, blockHash, pastUTXODiff, nil)
|
||||
if isNewSelectedTip {
|
||||
log.Debugf("Block %s is the new selected tip, therefore setting it as old selected tip's diffChild", blockHash)
|
||||
|
||||
updatedOldSelectedTipUTXOSet, err := pastUTXOSet.DiffFrom(oldSelectedTipUTXOSet)
|
||||
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 {
|
||||
log.Debugf("Block %s is the tip of currently resolved chain, but not the new selected tip,"+
|
||||
"therefore setting it's utxoDiffChild to be the current selectedTip %s", blockHash, oldSelectedTip)
|
||||
utxoDiff, err := oldSelectedTipUTXOSet.DiffFrom(pastUTXOSet)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
csm.stageDiff(stagingArea, blockHash, utxoDiff, oldSelectedTip)
|
||||
}
|
||||
} else {
|
||||
log.Debugf("Block %s is not the new SelectedTip, therefore setting old selectedTip as it's diffChild", blockHash)
|
||||
pastUTXODiff, err = oldSelectedTipUTXOSet.DiffFrom(pastUTXODiff)
|
||||
// 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, err
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
log.Tracef("Staging the utxoDiff of block %s", blockHash)
|
||||
csm.stageDiff(stagingArea, blockHash, pastUTXODiff, oldSelectedTip)
|
||||
csm.stageDiff(stagingArea, blockHash, utxoDiff, selectedParentHash)
|
||||
}
|
||||
|
||||
return externalapi.StatusUTXOValid, nil
|
||||
return externalapi.StatusUTXOValid, pastUTXOSet, nil
|
||||
}
|
||||
|
||||
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,
|
||||
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++ {
|
||||
lastBlock, _, err = tc.AddBlock([]*externalapi.DomainHash{lastBlock}, nil, 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)
|
||||
@ -98,7 +98,7 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
|
||||
for i := 0; i < int(consensusConfig.MaxBlockParents); i++ {
|
||||
block, _, err := tc.AddBlock([]*externalapi.DomainHash{virtualSelectedParent}, nil, 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)
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
)
|
||||
|
||||
// RenderDAGToDot is a helper function for debugging tests.
|
||||
@ -28,6 +30,7 @@ func (tc *testConsensus) convertToDot() (string, error) {
|
||||
}
|
||||
defer blocksIterator.Close()
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
for ok := blocksIterator.First(); ok; ok = blocksIterator.Next() {
|
||||
hash, err := blocksIterator.Get()
|
||||
if err != nil {
|
||||
@ -35,7 +38,7 @@ func (tc *testConsensus) convertToDot() (string, error) {
|
||||
}
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
|
@ -34,23 +34,6 @@ func checkIntersectionWithRule(collection1 utxoCollection, collection2 utxoColle
|
||||
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
|
||||
// having same DAA score, puts it into result and into remainder from collection1
|
||||
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
|
||||
// having same DAA score, puts it into result
|
||||
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
|
||||
// having same DAA score, puts it into result and into remainder from collection1
|
||||
func subtractionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
|
||||
@ -175,13 +139,13 @@ func diffFrom(this, other *mutableUTXODiff) (*mutableUTXODiff, error) {
|
||||
}
|
||||
|
||||
result := &mutableUTXODiff{
|
||||
toAdd: make(utxoCollection, len(this.toRemove)+len(other.toAdd)),
|
||||
toRemove: make(utxoCollection, len(this.toAdd)+len(other.toRemove)),
|
||||
toAdd: make(utxoCollection),
|
||||
toRemove: make(utxoCollection),
|
||||
}
|
||||
|
||||
// All transactions in this.toAdd:
|
||||
// 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)
|
||||
// If they are in other.toRemove - base utxoSet is not the same
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
intersectionWithRemainderHavingDAAScoreInPlace(other.toRemove, this.toAdd, intersection, this.toRemove)
|
||||
// If already exists in toAdd with the same DAA score - remove from toAdd
|
||||
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
|
||||
intersectionWithRemainderHavingDAAScoreInPlace(other.toAdd, this.toRemove, intersection, this.toAdd)
|
||||
// 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 {
|
||||
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()
|
||||
@ -21,7 +21,7 @@ func (iud *immutableUTXODiff) ToAdd() externalapi.UTXOCollection {
|
||||
|
||||
func (iud *immutableUTXODiff) ToRemove() externalapi.UTXOCollection {
|
||||
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()
|
||||
@ -29,7 +29,7 @@ func (iud *immutableUTXODiff) ToRemove() externalapi.UTXOCollection {
|
||||
|
||||
func (iud *immutableUTXODiff) WithDiff(other externalapi.UTXODiff) (externalapi.UTXODiff, error) {
|
||||
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)
|
||||
@ -37,7 +37,7 @@ func (iud *immutableUTXODiff) WithDiff(other externalapi.UTXODiff) (externalapi.
|
||||
|
||||
func (iud *immutableUTXODiff) DiffFrom(other externalapi.UTXODiff) (externalapi.UTXODiff, error) {
|
||||
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)
|
||||
@ -74,9 +74,22 @@ func NewUTXODiffFromCollections(toAdd, toRemove externalapi.UTXOCollection) (ext
|
||||
}
|
||||
|
||||
func (iud *immutableUTXODiff) CloneMutable() externalapi.MutableUTXODiff {
|
||||
if iud.isInvalidated {
|
||||
panic(errors.New("Attempt to read from an invalidated UTXODiff"))
|
||||
}
|
||||
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 {
|
||||
if iud == nil {
|
||||
return nil
|
||||
@ -84,3 +97,7 @@ func (iud *immutableUTXODiff) cloneMutable() *mutableUTXODiff {
|
||||
|
||||
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 {
|
||||
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) {
|
||||
consensusConfig := consensus.Config{Params: dagconfig.DevnetParams}
|
||||
consensusConfig.SkipProofOfWork = true
|
||||
consensusConfig.DisableDifficultyAdjustment = true
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(&consensusConfig, "ReorgHonest")
|
||||
@ -130,7 +131,7 @@ func testReorg(cfg *configFlags) {
|
||||
doneChan <- struct{}{}
|
||||
})
|
||||
|
||||
const timeout = 10 * time.Minute
|
||||
const timeout = 12 * time.Hour
|
||||
select {
|
||||
case <-doneChan:
|
||||
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 "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 "Running mempool-limits"
|
||||
|
Loading…
x
Reference in New Issue
Block a user