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:
Svarog 2021-04-20 10:26:55 +03:00 committed by GitHub
parent 28bfc0fb9c
commit dfd8b3423d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 442 additions and 124 deletions

View File

@ -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

View File

@ -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)

View File

@ -14,6 +14,7 @@ type UTXODiff interface {
ToRemove() UTXOCollection
WithDiff(other UTXODiff) (UTXODiff, error)
DiffFrom(other UTXODiff) (UTXODiff, error)
Reversed() UTXODiff
CloneMutable() MutableUTXODiff
}

View File

@ -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
}

View File

@ -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)
}

View 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
}

View File

@ -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

View File

@ -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(

View File

@ -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)
}

View File

@ -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,

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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

View File

@ -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()
}

View File

@ -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,
}
}

View File

@ -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):

View 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

View File

@ -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"