Unite reachability stores (#1963)

* Unite all reachability stores

* Upgrade script

* Fix tests

* Add UpdateReindexRoot to RebuildReachability

* Use dbTx when deleting reachability stores

* Use ghostdagDataWithoutPrunedBlocks when rebuilding reachability

* Use next tree ancestor wherever possible and avoid finality point search if the block is too close to pruning point

* Address the boundary case where the pruning point becomes the finality point

* some minor fixes

* Remove RebuildReachability and use manual syncing between old and new consensus for migration

* Remove sanity test (it failed when tips where not in the same order)

Co-authored-by: msutton <mikisiton2@gmail.com>
This commit is contained in:
Ori Newman 2022-03-27 21:23:03 +03:00 committed by GitHub
parent 639183ba0e
commit 58d627e05a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 705 additions and 147 deletions

View File

@ -35,7 +35,7 @@ type consensus struct {
headerTipsManager model.HeadersSelectedTipManager headerTipsManager model.HeadersSelectedTipManager
mergeDepthManager model.MergeDepthManager mergeDepthManager model.MergeDepthManager
pruningManager model.PruningManager pruningManager model.PruningManager
reachabilityManagers []model.ReachabilityManager reachabilityManager model.ReachabilityManager
finalityManager model.FinalityManager finalityManager model.FinalityManager
pruningProofManager model.PruningProofManager pruningProofManager model.PruningProofManager
@ -49,7 +49,7 @@ type consensus struct {
consensusStateStore model.ConsensusStateStore consensusStateStore model.ConsensusStateStore
headersSelectedTipStore model.HeaderSelectedTipStore headersSelectedTipStore model.HeaderSelectedTipStore
multisetStore model.MultisetStore multisetStore model.MultisetStore
reachabilityDataStores []model.ReachabilityDataStore reachabilityDataStore model.ReachabilityDataStore
utxoDiffStore model.UTXODiffStore utxoDiffStore model.UTXODiffStore
finalityStore model.FinalityStore finalityStore model.FinalityStore
headersSelectedChainStore model.HeadersSelectedChainStore headersSelectedChainStore model.HeadersSelectedChainStore
@ -83,11 +83,9 @@ func (s *consensus) Init(skipAddingGenesis bool) error {
// on a node with pruned header all blocks without known parents points to it. // on a node with pruned header all blocks without known parents points to it.
if !exists { if !exists {
s.blockStatusStore.Stage(stagingArea, model.VirtualGenesisBlockHash, externalapi.StatusUTXOValid) s.blockStatusStore.Stage(stagingArea, model.VirtualGenesisBlockHash, externalapi.StatusUTXOValid)
for _, reachabilityManager := range s.reachabilityManagers { err = s.reachabilityManager.Init(stagingArea)
err = reachabilityManager.Init(stagingArea) if err != nil {
if err != nil { return err
return err
}
} }
for _, dagTopologyManager := range s.dagTopologyManagers { for _, dagTopologyManager := range s.dagTopologyManagers {

View File

@ -75,6 +75,11 @@ func (brs *blockRelationStore) Has(dbContext model.DBReader, stagingArea *model.
return dbContext.Has(brs.hashAsKey(blockHash)) return dbContext.Has(brs.hashAsKey(blockHash))
} }
func (brs *blockRelationStore) UnstageAll(stagingArea *model.StagingArea) {
stagingShard := brs.stagingShard(stagingArea)
stagingShard.toAdd = make(map[externalapi.DomainHash]*model.BlockRelations)
}
func (brs *blockRelationStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { func (brs *blockRelationStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
return brs.bucket.Key(hash.ByteSlice()) return brs.bucket.Key(hash.ByteSlice())
} }

View File

@ -69,6 +69,12 @@ func (gds *ghostdagDataStore) Get(dbContext model.DBReader, stagingArea *model.S
return blockGHOSTDAGData, nil return blockGHOSTDAGData, nil
} }
func (gds *ghostdagDataStore) UnstageAll(stagingArea *model.StagingArea) {
stagingShard := gds.stagingShard(stagingArea)
stagingShard.toAdd = make(map[key]*externalapi.BlockGHOSTDAGData)
}
func (gds *ghostdagDataStore) serializeKey(k key) model.DBKey { func (gds *ghostdagDataStore) serializeKey(k key) model.DBKey {
if k.isTrustedData { if k.isTrustedData {
return gds.trustedDataBucket.Key(k.hash.ByteSlice()) return gds.trustedDataBucket.Key(k.hash.ByteSlice())

View File

@ -41,6 +41,27 @@ func (rds *reachabilityDataStore) StageReachabilityData(stagingArea *model.Stagi
stagingShard.reachabilityData[*blockHash] = reachabilityData stagingShard.reachabilityData[*blockHash] = reachabilityData
} }
func (rds *reachabilityDataStore) Delete(dbContext model.DBWriter) error {
cursor, err := dbContext.Cursor(rds.reachabilityDataBucket)
if err != nil {
return err
}
for ok := cursor.First(); ok; ok = cursor.Next() {
key, err := cursor.Key()
if err != nil {
return err
}
err = dbContext.Delete(key)
if err != nil {
return err
}
}
return dbContext.Delete(rds.reachabilityReindexRootKey)
}
// StageReachabilityReindexRoot stages the given reachabilityReindexRoot // StageReachabilityReindexRoot stages the given reachabilityReindexRoot
func (rds *reachabilityDataStore) StageReachabilityReindexRoot(stagingArea *model.StagingArea, reachabilityReindexRoot *externalapi.DomainHash) { func (rds *reachabilityDataStore) StageReachabilityReindexRoot(stagingArea *model.StagingArea, reachabilityReindexRoot *externalapi.DomainHash) {
stagingShard := rds.stagingShard(stagingArea) stagingShard := rds.stagingShard(stagingArea)

View File

@ -1,16 +1,17 @@
package consensus package consensus
import ( import (
"io/ioutil"
"os"
"sync"
"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockwindowheapslicestore" "github.com/kaspanet/kaspad/domain/consensus/datastructures/blockwindowheapslicestore"
"github.com/kaspanet/kaspad/domain/consensus/datastructures/daawindowstore" "github.com/kaspanet/kaspad/domain/consensus/datastructures/daawindowstore"
"github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/processes/blockparentbuilder" "github.com/kaspanet/kaspad/domain/consensus/processes/blockparentbuilder"
parentssanager "github.com/kaspanet/kaspad/domain/consensus/processes/parentsmanager" parentssanager "github.com/kaspanet/kaspad/domain/consensus/processes/parentsmanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/pruningproofmanager" "github.com/kaspanet/kaspad/domain/consensus/processes/pruningproofmanager"
"github.com/pkg/errors"
"io/ioutil"
"os"
"sync"
"github.com/kaspanet/kaspad/domain/prefixmanager/prefix" "github.com/kaspanet/kaspad/domain/prefixmanager/prefix"
"github.com/kaspanet/kaspad/util/txmass" "github.com/kaspanet/kaspad/util/txmass"
@ -74,7 +75,7 @@ type Config struct {
// Factory instantiates new Consensuses // Factory instantiates new Consensuses
type Factory interface { type Factory interface {
NewConsensus(config *Config, db infrastructuredatabase.Database, dbPrefix *prefix.Prefix) ( NewConsensus(config *Config, db infrastructuredatabase.Database, dbPrefix *prefix.Prefix) (
externalapi.Consensus, error) externalapi.Consensus, bool, error)
NewTestConsensus(config *Config, testName string) ( NewTestConsensus(config *Config, testName string) (
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error) tc testapi.TestConsensus, teardown func(keepDataDir bool), err error)
@ -106,7 +107,7 @@ func NewFactory() Factory {
// NewConsensus instantiates a new Consensus // NewConsensus instantiates a new Consensus
func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Database, dbPrefix *prefix.Prefix) ( func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Database, dbPrefix *prefix.Prefix) (
externalapi.Consensus, error) { consensusInstance externalapi.Consensus, shouldMigrate bool, err error) {
dbManager := consensusdatabase.New(db) dbManager := consensusdatabase.New(db)
prefixBucket := consensusdatabase.MakeBucket(dbPrefix.Serialize()) prefixBucket := consensusdatabase.MakeBucket(dbPrefix.Serialize())
@ -129,11 +130,11 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
acceptanceDataStore := acceptancedatastore.New(prefixBucket, 200, preallocateCaches) acceptanceDataStore := acceptancedatastore.New(prefixBucket, 200, preallocateCaches)
blockStore, err := blockstore.New(dbManager, prefixBucket, 200, preallocateCaches) blockStore, err := blockstore.New(dbManager, prefixBucket, 200, preallocateCaches)
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
blockHeaderStore, err := blockheaderstore.New(dbManager, prefixBucket, 10_000, preallocateCaches) blockHeaderStore, err := blockheaderstore.New(dbManager, prefixBucket, 10_000, preallocateCaches)
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
blockStatusStore := blockstatusstore.New(prefixBucket, pruningWindowSizePlusFinalityDepthForCache, preallocateCaches) blockStatusStore := blockstatusstore.New(prefixBucket, pruningWindowSizePlusFinalityDepthForCache, preallocateCaches)
@ -148,11 +149,35 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
daaBlocksStore := daablocksstore.New(prefixBucket, pruningWindowSizeForCaches, int(config.FinalityDepth()), preallocateCaches) daaBlocksStore := daablocksstore.New(prefixBucket, pruningWindowSizeForCaches, int(config.FinalityDepth()), preallocateCaches)
windowHeapSliceStore := blockwindowheapslicestore.New(2000, preallocateCaches) windowHeapSliceStore := blockwindowheapslicestore.New(2000, preallocateCaches)
newReachabilityDataStore := reachabilitydatastore.New(prefixBucket, pruningWindowSizePlusFinalityDepthForCache*2, preallocateCaches)
blockRelationStores, reachabilityDataStores, ghostdagDataStores := dagStores(config, prefixBucket, pruningWindowSizePlusFinalityDepthForCache, pruningWindowSizeForCaches, preallocateCaches) blockRelationStores, reachabilityDataStores, ghostdagDataStores := dagStores(config, prefixBucket, pruningWindowSizePlusFinalityDepthForCache, pruningWindowSizeForCaches, preallocateCaches)
reachabilityManagers, dagTopologyManagers, ghostdagManagers, dagTraversalManagers := f.dagProcesses(config, dbManager, blockHeaderStore, daaWindowStore, windowHeapSliceStore, blockRelationStores, reachabilityDataStores, ghostdagDataStores) oldReachabilityManager := reachabilitymanager.New(
dbManager,
ghostdagDataStores[0],
reachabilityDataStores[0])
isOldReachabilityInitialized, err := reachabilityDataStores[0].HasReachabilityData(dbManager, model.NewStagingArea(), model.VirtualGenesisBlockHash)
if err != nil {
return nil, false, err
}
newReachabilityManager := reachabilitymanager.New(
dbManager,
ghostdagDataStores[0],
newReachabilityDataStore)
reachabilityManager := newReachabilityManager
if isOldReachabilityInitialized {
reachabilityManager = oldReachabilityManager
} else {
for i := range reachabilityDataStores {
reachabilityDataStores[i] = newReachabilityDataStore
}
}
reachabilityDataStore := reachabilityDataStores[0]
dagTopologyManagers, ghostdagManagers, dagTraversalManagers := f.dagProcesses(config, dbManager, blockHeaderStore, daaWindowStore, windowHeapSliceStore, blockRelationStores, reachabilityDataStores, ghostdagDataStores, isOldReachabilityInitialized)
blockRelationStore := blockRelationStores[0] blockRelationStore := blockRelationStores[0]
reachabilityDataStore := reachabilityDataStores[0]
ghostdagDataStore := ghostdagDataStores[0] ghostdagDataStore := ghostdagDataStores[0]
dagTopologyManager := dagTopologyManagers[0] dagTopologyManager := dagTopologyManagers[0]
@ -227,6 +252,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
dagTopologyManager, dagTopologyManager,
finalityStore, finalityStore,
ghostdagDataStore, ghostdagDataStore,
pruningStore,
genesisHash, genesisHash,
config.FinalityDepth()) config.FinalityDepth())
mergeDepthManager := mergedepthmanager.New( mergeDepthManager := mergedepthmanager.New(
@ -264,7 +290,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
pruningStore, pruningStore,
daaBlocksStore) daaBlocksStore)
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
pruningManager := pruningmanager.New( pruningManager := pruningmanager.New(
@ -319,7 +345,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
dagTraversalManager, dagTraversalManager,
coinbaseManager, coinbaseManager,
mergeDepthManager, mergeDepthManager,
reachabilityManagers, reachabilityManager,
finalityManager, finalityManager,
blockParentBuilder, blockParentBuilder,
pruningManager, pruningManager,
@ -383,7 +409,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
pruningManager, pruningManager,
blockValidator, blockValidator,
dagTopologyManager, dagTopologyManager,
reachabilityManagers, reachabilityManager,
difficultyManager, difficultyManager,
pastMedianTimeManager, pastMedianTimeManager,
coinbaseManager, coinbaseManager,
@ -411,9 +437,10 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
dbManager, dbManager,
dagTopologyManagers, dagTopologyManagers,
ghostdagManagers, ghostdagManagers,
reachabilityManagers, reachabilityManager,
dagTraversalManagers, dagTraversalManagers,
parentsManager, parentsManager,
pruningManager,
ghostdagDataStores, ghostdagDataStores,
pruningStore, pruningStore,
@ -421,6 +448,8 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
blockStatusStore, blockStatusStore,
finalityStore, finalityStore,
consensusStateStore, consensusStateStore,
blockRelationStore,
reachabilityDataStore,
genesisHash, genesisHash,
config.K, config.K,
@ -450,7 +479,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
headerTipsManager: headerTipsManager, headerTipsManager: headerTipsManager,
mergeDepthManager: mergeDepthManager, mergeDepthManager: mergeDepthManager,
pruningManager: pruningManager, pruningManager: pruningManager,
reachabilityManagers: reachabilityManagers, reachabilityManager: reachabilityManager,
finalityManager: finalityManager, finalityManager: finalityManager,
pruningProofManager: pruningProofManager, pruningProofManager: pruningProofManager,
@ -464,7 +493,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
consensusStateStore: consensusStateStore, consensusStateStore: consensusStateStore,
headersSelectedTipStore: headersSelectedTipStore, headersSelectedTipStore: headersSelectedTipStore,
multisetStore: multisetStore, multisetStore: multisetStore,
reachabilityDataStores: reachabilityDataStores, reachabilityDataStore: reachabilityDataStore,
utxoDiffStore: utxoDiffStore, utxoDiffStore: utxoDiffStore,
finalityStore: finalityStore, finalityStore: finalityStore,
headersSelectedChainStore: headersSelectedChainStore, headersSelectedChainStore: headersSelectedChainStore,
@ -472,25 +501,29 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
blocksWithTrustedDataDAAWindowStore: daaWindowStore, blocksWithTrustedDataDAAWindowStore: daaWindowStore,
} }
if isOldReachabilityInitialized {
return c, true, nil
}
err = c.Init(config.SkipAddingGenesis) err = c.Init(config.SkipAddingGenesis)
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
err = consensusStateManager.RecoverUTXOIfRequired() err = consensusStateManager.RecoverUTXOIfRequired()
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
err = pruningManager.ClearImportedPruningPointData() err = pruningManager.ClearImportedPruningPointData()
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
err = pruningManager.UpdatePruningPointIfRequired() err = pruningManager.UpdatePruningPointIfRequired()
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
return c, nil return c, false, nil
} }
func (f *factory) NewTestConsensus(config *Config, testName string) ( func (f *factory) NewTestConsensus(config *Config, testName string) (
@ -517,11 +550,15 @@ func (f *factory) NewTestConsensus(config *Config, testName string) (
} }
testConsensusDBPrefix := &prefix.Prefix{} testConsensusDBPrefix := &prefix.Prefix{}
consensusAsInterface, err := f.NewConsensus(config, db, testConsensusDBPrefix) consensusAsInterface, shouldMigrate, err := f.NewConsensus(config, db, testConsensusDBPrefix)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if shouldMigrate {
return nil, nil, errors.Errorf("A fresh consensus should never return shouldMigrate=true")
}
consensusAsImplementation := consensusAsInterface.(*consensus) consensusAsImplementation := consensusAsInterface.(*consensus)
testConsensusStateManager := consensusstatemanager.NewTestConsensusStateManager(consensusAsImplementation.consensusStateManager) testConsensusStateManager := consensusstatemanager.NewTestConsensusStateManager(consensusAsImplementation.consensusStateManager)
testTransactionValidator := transactionvalidator.NewTestTransactionValidator(consensusAsImplementation.transactionValidator) testTransactionValidator := transactionvalidator.NewTestTransactionValidator(consensusAsImplementation.transactionValidator)
@ -532,7 +569,7 @@ func (f *factory) NewTestConsensus(config *Config, testName string) (
database: db, database: db,
testConsensusStateManager: testConsensusStateManager, testConsensusStateManager: testConsensusStateManager,
testReachabilityManager: reachabilitymanager.NewTestReachabilityManager(consensusAsImplementation. testReachabilityManager: reachabilitymanager.NewTestReachabilityManager(consensusAsImplementation.
reachabilityManagers[0]), reachabilityManager),
testTransactionValidator: testTransactionValidator, testTransactionValidator: testTransactionValidator,
} }
tstConsensus.testBlockBuilder = blockbuilder.NewTestBlockBuilder(consensusAsImplementation.blockBuilder, tstConsensus) tstConsensus.testBlockBuilder = blockbuilder.NewTestBlockBuilder(consensusAsImplementation.blockBuilder, tstConsensus)
@ -609,8 +646,8 @@ func (f *factory) dagProcesses(config *Config,
windowHeapSliceStore model.WindowHeapSliceStore, windowHeapSliceStore model.WindowHeapSliceStore,
blockRelationStores []model.BlockRelationStore, blockRelationStores []model.BlockRelationStore,
reachabilityDataStores []model.ReachabilityDataStore, reachabilityDataStores []model.ReachabilityDataStore,
ghostdagDataStores []model.GHOSTDAGDataStore) ( ghostdagDataStores []model.GHOSTDAGDataStore,
[]model.ReachabilityManager, isOldReachabilityInitialized bool) (
[]model.DAGTopologyManager, []model.DAGTopologyManager,
[]model.GHOSTDAGManager, []model.GHOSTDAGManager,
[]model.DAGTraversalManager, []model.DAGTraversalManager,
@ -621,11 +658,20 @@ func (f *factory) dagProcesses(config *Config,
ghostdagManagers := make([]model.GHOSTDAGManager, config.MaxBlockLevel+1) ghostdagManagers := make([]model.GHOSTDAGManager, config.MaxBlockLevel+1)
dagTraversalManagers := make([]model.DAGTraversalManager, config.MaxBlockLevel+1) dagTraversalManagers := make([]model.DAGTraversalManager, config.MaxBlockLevel+1)
newReachabilityManager := reachabilitymanager.New(
dbManager,
ghostdagDataStores[0],
reachabilityDataStores[0])
for i := 0; i <= config.MaxBlockLevel; i++ { for i := 0; i <= config.MaxBlockLevel; i++ {
reachabilityManagers[i] = reachabilitymanager.New( if isOldReachabilityInitialized {
dbManager, reachabilityManagers[i] = reachabilitymanager.New(
ghostdagDataStores[i], dbManager,
reachabilityDataStores[i]) ghostdagDataStores[i],
reachabilityDataStores[i])
} else {
reachabilityManagers[i] = newReachabilityManager
}
dagTopologyManagers[i] = dagtopologymanager.New( dagTopologyManagers[i] = dagtopologymanager.New(
dbManager, dbManager,
@ -645,7 +691,7 @@ func (f *factory) dagProcesses(config *Config,
dbManager, dbManager,
dagTopologyManagers[i], dagTopologyManagers[i],
ghostdagDataStores[i], ghostdagDataStores[i],
reachabilityDataStores[i], reachabilityManagers[i],
ghostdagManagers[i], ghostdagManagers[i],
daaWindowStore, daaWindowStore,
windowHeapSliceStore, windowHeapSliceStore,
@ -653,5 +699,5 @@ func (f *factory) dagProcesses(config *Config,
config.DifficultyAdjustmentWindowSize) config.DifficultyAdjustmentWindowSize)
} }
return reachabilityManagers, dagTopologyManagers, ghostdagManagers, dagTraversalManagers return dagTopologyManagers, ghostdagManagers, dagTraversalManagers
} }

View File

@ -24,8 +24,12 @@ func TestNewConsensus(t *testing.T) {
t.Fatalf("error in NewLevelDB: %s", err) t.Fatalf("error in NewLevelDB: %s", err)
} }
_, err = f.NewConsensus(config, db, &prefix.Prefix{}) _, shouldMigrate, err := f.NewConsensus(config, db, &prefix.Prefix{})
if err != nil { if err != nil {
t.Fatalf("error in NewConsensus: %+v", err) t.Fatalf("error in NewConsensus: %+v", err)
} }
if shouldMigrate {
t.Fatalf("A fresh consensus should never return shouldMigrate=true")
}
} }

View File

@ -9,4 +9,5 @@ type BlockRelationStore interface {
IsStaged(stagingArea *StagingArea) bool IsStaged(stagingArea *StagingArea) bool
BlockRelation(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*BlockRelations, error) BlockRelation(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*BlockRelations, error)
Has(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash) (bool, error) Has(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash) (bool, error)
UnstageAll(stagingArea *StagingArea)
} }

View File

@ -8,4 +8,5 @@ type GHOSTDAGDataStore interface {
Stage(stagingArea *StagingArea, blockHash *externalapi.DomainHash, blockGHOSTDAGData *externalapi.BlockGHOSTDAGData, isTrustedData bool) Stage(stagingArea *StagingArea, blockHash *externalapi.DomainHash, blockGHOSTDAGData *externalapi.BlockGHOSTDAGData, isTrustedData bool)
IsStaged(stagingArea *StagingArea) bool IsStaged(stagingArea *StagingArea) bool
Get(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash, isTrustedData bool) (*externalapi.BlockGHOSTDAGData, error) Get(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash, isTrustedData bool) (*externalapi.BlockGHOSTDAGData, error)
UnstageAll(stagingArea *StagingArea)
} }

View File

@ -11,4 +11,5 @@ type ReachabilityDataStore interface {
ReachabilityData(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash) (ReachabilityData, error) ReachabilityData(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash) (ReachabilityData, error)
HasReachabilityData(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash) (bool, error) HasReachabilityData(dbContext DBReader, stagingArea *StagingArea, blockHash *externalapi.DomainHash) (bool, error)
ReachabilityReindexRoot(dbContext DBReader, stagingArea *StagingArea) (*externalapi.DomainHash, error) ReachabilityReindexRoot(dbContext DBReader, stagingArea *StagingArea) (*externalapi.DomainHash, error)
Delete(dbContext DBWriter) error
} }

View File

@ -13,7 +13,7 @@ type DAGTopologyManager interface {
IsAncestorOfAny(stagingArea *StagingArea, blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) IsAncestorOfAny(stagingArea *StagingArea, blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error)
IsAnyAncestorOf(stagingArea *StagingArea, potentialAncestors []*externalapi.DomainHash, blockHash *externalapi.DomainHash) (bool, error) IsAnyAncestorOf(stagingArea *StagingArea, potentialAncestors []*externalapi.DomainHash, blockHash *externalapi.DomainHash) (bool, error)
IsInSelectedParentChainOf(stagingArea *StagingArea, blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) IsInSelectedParentChainOf(stagingArea *StagingArea, blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
ChildInSelectedParentChainOf(stagingArea *StagingArea, context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) ChildInSelectedParentChainOf(stagingArea *StagingArea, lowHash, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
SetParents(stagingArea *StagingArea, blockHash *externalapi.DomainHash, parentHashes []*externalapi.DomainHash) error SetParents(stagingArea *StagingArea, blockHash *externalapi.DomainHash, parentHashes []*externalapi.DomainHash) error
} }

View File

@ -104,6 +104,7 @@ type FutureCoveringTreeNodeSet []*externalapi.DomainHash
// Clone returns a clone of FutureCoveringTreeNodeSet // Clone returns a clone of FutureCoveringTreeNodeSet
func (fctns FutureCoveringTreeNodeSet) Clone() FutureCoveringTreeNodeSet { func (fctns FutureCoveringTreeNodeSet) Clone() FutureCoveringTreeNodeSet {
//return fctns
return externalapi.CloneHashes(fctns) return externalapi.CloneHashes(fctns)
} }

View File

@ -22,7 +22,7 @@ type blockProcessor struct {
pruningManager model.PruningManager pruningManager model.PruningManager
blockValidator model.BlockValidator blockValidator model.BlockValidator
dagTopologyManager model.DAGTopologyManager dagTopologyManager model.DAGTopologyManager
reachabilityManagers []model.ReachabilityManager reachabilityManager model.ReachabilityManager
difficultyManager model.DifficultyManager difficultyManager model.DifficultyManager
pastMedianTimeManager model.PastMedianTimeManager pastMedianTimeManager model.PastMedianTimeManager
coinbaseManager model.CoinbaseManager coinbaseManager model.CoinbaseManager
@ -60,7 +60,7 @@ func New(
pruningManager model.PruningManager, pruningManager model.PruningManager,
blockValidator model.BlockValidator, blockValidator model.BlockValidator,
dagTopologyManager model.DAGTopologyManager, dagTopologyManager model.DAGTopologyManager,
reachabilityManagers []model.ReachabilityManager, reachabilityManager model.ReachabilityManager,
difficultyManager model.DifficultyManager, difficultyManager model.DifficultyManager,
pastMedianTimeManager model.PastMedianTimeManager, pastMedianTimeManager model.PastMedianTimeManager,
coinbaseManager model.CoinbaseManager, coinbaseManager model.CoinbaseManager,
@ -94,7 +94,7 @@ func New(
pruningManager: pruningManager, pruningManager: pruningManager,
blockValidator: blockValidator, blockValidator: blockValidator,
dagTopologyManager: dagTopologyManager, dagTopologyManager: dagTopologyManager,
reachabilityManagers: reachabilityManagers, reachabilityManager: reachabilityManager,
difficultyManager: difficultyManager, difficultyManager: difficultyManager,
pastMedianTimeManager: pastMedianTimeManager, pastMedianTimeManager: pastMedianTimeManager,
coinbaseManager: coinbaseManager, coinbaseManager: coinbaseManager,

View File

@ -254,19 +254,7 @@ func (bp *blockProcessor) updateReachabilityReindexRoot(stagingArea *model.Stagi
return nil return nil
} }
headersSelectedTipHeader, err := bp.blockHeaderStore.BlockHeader(bp.databaseContext, stagingArea, headersSelectedTip) return bp.reachabilityManager.UpdateReindexRoot(stagingArea, headersSelectedTip)
if err != nil {
return err
}
headersSelectedTipHeaderBlockLevel := headersSelectedTipHeader.BlockLevel(bp.maxBlockLevel)
for blockLevel := 0; blockLevel <= headersSelectedTipHeaderBlockLevel; blockLevel++ {
err := bp.reachabilityManagers[blockLevel].UpdateReindexRoot(stagingArea, headersSelectedTip)
if err != nil {
return err
}
}
return nil
} }
func (bp *blockProcessor) checkBlockStatus(stagingArea *model.StagingArea, block *externalapi.DomainBlock) error { func (bp *blockProcessor) checkBlockStatus(stagingArea *model.StagingArea, block *externalapi.DomainBlock) error {

View File

@ -62,12 +62,9 @@ func (v *blockValidator) ValidateHeaderInContext(stagingArea *model.StagingArea,
return err return err
} }
if !hasReachabilityData { if !hasReachabilityData {
blockLevel := header.BlockLevel(v.maxBlockLevel) err = v.reachabilityManager.AddBlock(stagingArea, blockHash)
for i := 0; i <= blockLevel; i++ { if err != nil {
err = v.reachabilityManagers[i].AddBlock(stagingArea, blockHash) return err
if err != nil {
return err
}
} }
} }

View File

@ -37,7 +37,7 @@ type blockValidator struct {
coinbaseManager model.CoinbaseManager coinbaseManager model.CoinbaseManager
mergeDepthManager model.MergeDepthManager mergeDepthManager model.MergeDepthManager
pruningStore model.PruningStore pruningStore model.PruningStore
reachabilityManagers []model.ReachabilityManager reachabilityManager model.ReachabilityManager
finalityManager model.FinalityManager finalityManager model.FinalityManager
blockParentBuilder model.BlockParentBuilder blockParentBuilder model.BlockParentBuilder
pruningManager model.PruningManager pruningManager model.PruningManager
@ -77,7 +77,7 @@ func New(powMax *big.Int,
dagTraversalManager model.DAGTraversalManager, dagTraversalManager model.DAGTraversalManager,
coinbaseManager model.CoinbaseManager, coinbaseManager model.CoinbaseManager,
mergeDepthManager model.MergeDepthManager, mergeDepthManager model.MergeDepthManager,
reachabilityManagers []model.ReachabilityManager, reachabilityManager model.ReachabilityManager,
finalityManager model.FinalityManager, finalityManager model.FinalityManager,
blockParentBuilder model.BlockParentBuilder, blockParentBuilder model.BlockParentBuilder,
pruningManager model.PruningManager, pruningManager model.PruningManager,
@ -118,7 +118,7 @@ func New(powMax *big.Int,
dagTraversalManager: dagTraversalManager, dagTraversalManager: dagTraversalManager,
coinbaseManager: coinbaseManager, coinbaseManager: coinbaseManager,
mergeDepthManager: mergeDepthManager, mergeDepthManager: mergeDepthManager,
reachabilityManagers: reachabilityManagers, reachabilityManager: reachabilityManager,
finalityManager: finalityManager, finalityManager: finalityManager,
blockParentBuilder: blockParentBuilder, blockParentBuilder: blockParentBuilder,
pruningManager: pruningManager, pruningManager: pruningManager,

View File

@ -105,6 +105,17 @@ func (dtm *dagTopologyManager) IsAnyAncestorOf(stagingArea *model.StagingArea, p
// IsInSelectedParentChainOf returns true if blockHashA is in the selected parent chain of blockHashB // IsInSelectedParentChainOf returns true if blockHashA is in the selected parent chain of blockHashB
func (dtm *dagTopologyManager) IsInSelectedParentChainOf(stagingArea *model.StagingArea, blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) { func (dtm *dagTopologyManager) IsInSelectedParentChainOf(stagingArea *model.StagingArea, blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) {
// Virtual doesn't have reachability data, therefore, it should be treated as a special case -
// use its selected parent as blockHashB.
if blockHashB == model.VirtualBlockHash {
ghostdagData, err := dtm.ghostdagStore.Get(dtm.databaseContext, stagingArea, blockHashB, false)
if err != nil {
return false, err
}
blockHashB = ghostdagData.SelectedParent()
}
return dtm.reachabilityManager.IsReachabilityTreeAncestorOf(stagingArea, blockHashA, blockHashB) return dtm.reachabilityManager.IsReachabilityTreeAncestorOf(stagingArea, blockHashA, blockHashB)
} }
@ -176,12 +187,11 @@ func (dtm *dagTopologyManager) SetParents(stagingArea *model.StagingArea, blockH
return nil return nil
} }
// ChildInSelectedParentChainOf returns the child of `context` that is in the selected-parent-chain of `highHash` // ChildInSelectedParentChainOf returns the child of `lowHash` that is in the selected-parent-chain of `highHash`
func (dtm *dagTopologyManager) ChildInSelectedParentChainOf(stagingArea *model.StagingArea, func (dtm *dagTopologyManager) ChildInSelectedParentChainOf(stagingArea *model.StagingArea, lowHash, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
// Virtual doesn't have reachability data, therefore, it should be treated as a special case - // Virtual doesn't have reachability data, therefore, it should be treated as a special case -
// use it's selected parent as highHash. // use its selected parent as highHash.
specifiedHighHash := highHash specifiedHighHash := highHash
if highHash == model.VirtualBlockHash { if highHash == model.VirtualBlockHash {
ghostdagData, err := dtm.ghostdagStore.Get(dtm.databaseContext, stagingArea, highHash, false) ghostdagData, err := dtm.ghostdagStore.Get(dtm.databaseContext, stagingArea, highHash, false)
@ -191,20 +201,20 @@ func (dtm *dagTopologyManager) ChildInSelectedParentChainOf(stagingArea *model.S
selectedParent := ghostdagData.SelectedParent() selectedParent := ghostdagData.SelectedParent()
// In case where `context` is an immediate parent of `highHash` // In case where `context` is an immediate parent of `highHash`
if context.Equal(selectedParent) { if lowHash.Equal(selectedParent) {
return highHash, nil return highHash, nil
} }
highHash = selectedParent highHash = selectedParent
} }
isInSelectedParentChain, err := dtm.IsInSelectedParentChainOf(stagingArea, context, highHash) isInSelectedParentChain, err := dtm.IsInSelectedParentChainOf(stagingArea, lowHash, highHash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !isInSelectedParentChain { if !isInSelectedParentChain {
return nil, errors.Errorf("context(%s) is not in the selected-parent-chain of highHash(%s)", return nil, errors.Errorf("Claimed chain ancestor (%s) is not in the selected-parent-chain of highHash (%s)",
context, specifiedHighHash) lowHash, specifiedHighHash)
} }
return dtm.reachabilityManager.FindNextAncestor(stagingArea, highHash, context) return dtm.reachabilityManager.FindNextAncestor(stagingArea, highHash, lowHash)
} }

View File

@ -14,7 +14,7 @@ type dagTraversalManager struct {
dagTopologyManager model.DAGTopologyManager dagTopologyManager model.DAGTopologyManager
ghostdagManager model.GHOSTDAGManager ghostdagManager model.GHOSTDAGManager
ghostdagDataStore model.GHOSTDAGDataStore ghostdagDataStore model.GHOSTDAGDataStore
reachabilityDataStore model.ReachabilityDataStore reachabilityManager model.ReachabilityManager
daaWindowStore model.BlocksWithTrustedDataDAAWindowStore daaWindowStore model.BlocksWithTrustedDataDAAWindowStore
genesisHash *externalapi.DomainHash genesisHash *externalapi.DomainHash
difficultyAdjustmentWindowSize int difficultyAdjustmentWindowSize int
@ -26,19 +26,19 @@ func New(
databaseContext model.DBReader, databaseContext model.DBReader,
dagTopologyManager model.DAGTopologyManager, dagTopologyManager model.DAGTopologyManager,
ghostdagDataStore model.GHOSTDAGDataStore, ghostdagDataStore model.GHOSTDAGDataStore,
reachabilityDataStore model.ReachabilityDataStore, reachabilityManager model.ReachabilityManager,
ghostdagManager model.GHOSTDAGManager, ghostdagManager model.GHOSTDAGManager,
daaWindowStore model.BlocksWithTrustedDataDAAWindowStore, daaWindowStore model.BlocksWithTrustedDataDAAWindowStore,
windowHeapSliceStore model.WindowHeapSliceStore, windowHeapSliceStore model.WindowHeapSliceStore,
genesisHash *externalapi.DomainHash, genesisHash *externalapi.DomainHash,
difficultyAdjustmentWindowSize int) model.DAGTraversalManager { difficultyAdjustmentWindowSize int) model.DAGTraversalManager {
return &dagTraversalManager{ return &dagTraversalManager{
databaseContext: databaseContext, databaseContext: databaseContext,
dagTopologyManager: dagTopologyManager, dagTopologyManager: dagTopologyManager,
ghostdagDataStore: ghostdagDataStore, ghostdagDataStore: ghostdagDataStore,
reachabilityDataStore: reachabilityDataStore, reachabilityManager: reachabilityManager,
ghostdagManager: ghostdagManager, ghostdagManager: ghostdagManager,
daaWindowStore: daaWindowStore, daaWindowStore: daaWindowStore,
genesisHash: genesisHash, genesisHash: genesisHash,
difficultyAdjustmentWindowSize: difficultyAdjustmentWindowSize, difficultyAdjustmentWindowSize: difficultyAdjustmentWindowSize,

View File

@ -97,25 +97,13 @@ func (dtm *dagTraversalManager) SelectedChildIterator(stagingArea *model.Staging
var errNoSelectedChild = errors.New("errNoSelectedChild") var errNoSelectedChild = errors.New("errNoSelectedChild")
func (dtm *dagTraversalManager) SelectedChild(stagingArea *model.StagingArea, func (dtm *dagTraversalManager) SelectedChild(stagingArea *model.StagingArea,
context, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) { highHash, lowHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
data, err := dtm.reachabilityDataStore.ReachabilityData(dtm.databaseContext, stagingArea, blockHash) // The selected child is in fact the next reachability tree nextAncestor
nextAncestor, err := dtm.reachabilityManager.FindNextAncestor(stagingArea, highHash, lowHash)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrapf(errNoSelectedChild, "no selected child for %s from the point of view of %s",
lowHash, highHash)
} }
return nextAncestor, nil
for _, child := range data.Children() {
isChildInSelectedParentChainOfHighHash, err := dtm.dagTopologyManager.IsInSelectedParentChainOf(
stagingArea, child, context)
if err != nil {
return nil, err
}
if isChildInSelectedParentChainOfHighHash {
return child, nil
}
}
return nil, errors.Wrapf(errNoSelectedChild, "no selected child for %s from the point of view of %s",
blockHash, context)
} }

View File

@ -13,6 +13,7 @@ type finalityManager struct {
dagTopologyManager model.DAGTopologyManager dagTopologyManager model.DAGTopologyManager
finalityStore model.FinalityStore finalityStore model.FinalityStore
ghostdagDataStore model.GHOSTDAGDataStore ghostdagDataStore model.GHOSTDAGDataStore
pruningStore model.PruningStore
genesisHash *externalapi.DomainHash genesisHash *externalapi.DomainHash
finalityDepth uint64 finalityDepth uint64
} }
@ -22,6 +23,7 @@ func New(databaseContext model.DBReader,
dagTopologyManager model.DAGTopologyManager, dagTopologyManager model.DAGTopologyManager,
finalityStore model.FinalityStore, finalityStore model.FinalityStore,
ghostdagDataStore model.GHOSTDAGDataStore, ghostdagDataStore model.GHOSTDAGDataStore,
pruningStore model.PruningStore,
genesisHash *externalapi.DomainHash, genesisHash *externalapi.DomainHash,
finalityDepth uint64) model.FinalityManager { finalityDepth uint64) model.FinalityManager {
@ -31,6 +33,7 @@ func New(databaseContext model.DBReader,
dagTopologyManager: dagTopologyManager, dagTopologyManager: dagTopologyManager,
finalityStore: finalityStore, finalityStore: finalityStore,
ghostdagDataStore: ghostdagDataStore, ghostdagDataStore: ghostdagDataStore,
pruningStore: pruningStore,
finalityDepth: finalityDepth, finalityDepth: finalityDepth,
} }
} }
@ -96,6 +99,27 @@ func (fm *finalityManager) calculateFinalityPoint(stagingArea *model.StagingArea
return fm.genesisHash, nil return fm.genesisHash, nil
} }
pruningPoint, err := fm.pruningStore.PruningPoint(fm.databaseContext, stagingArea)
if err != nil {
return nil, err
}
pruningPointGhostdagData, err := fm.ghostdagDataStore.Get(fm.databaseContext, stagingArea, pruningPoint, false)
if err != nil {
return nil, err
}
if ghostdagData.BlueScore() < pruningPointGhostdagData.BlueScore()+fm.finalityDepth {
log.Debugf("%s blue score less than finality distance over pruning point - returning virtual genesis as finality point", blockHash)
return model.VirtualGenesisBlockHash, nil
}
isPruningPointOnChain, err := fm.dagTopologyManager.IsInSelectedParentChainOf(stagingArea, pruningPoint, blockHash)
if err != nil {
return nil, err
}
if !isPruningPointOnChain {
log.Debugf("pruning point not in selected chain of %s - returning virtual genesis as finality point", blockHash)
return model.VirtualGenesisBlockHash, nil
}
selectedParent := ghostdagData.SelectedParent() selectedParent := ghostdagData.SelectedParent()
if selectedParent.Equal(fm.genesisHash) { if selectedParent.Equal(fm.genesisHash) {
return fm.genesisHash, nil return fm.genesisHash, nil
@ -105,6 +129,12 @@ func (fm *finalityManager) calculateFinalityPoint(stagingArea *model.StagingArea
if err != nil { if err != nil {
return nil, err return nil, err
} }
// In this case we expect the pruning point or a block above it to be the finality point.
// Note that above we already verified the chain and distance conditions for this
if current.Equal(model.VirtualGenesisBlockHash) {
current = pruningPoint
}
requiredBlueScore := ghostdagData.BlueScore() - fm.finalityDepth requiredBlueScore := ghostdagData.BlueScore() - fm.finalityDepth
log.Debugf("%s's finality point is the one having the highest blue score lower then %d", blockHash, requiredBlueScore) log.Debugf("%s's finality point is the one having the highest blue score lower then %d", blockHash, requiredBlueScore)

View File

@ -320,10 +320,6 @@ func (ds *GHOSTDAGDataStoreImpl) IsStaged(*model.StagingArea) bool {
panic("implement me") panic("implement me")
} }
func (ds *GHOSTDAGDataStoreImpl) Discard() {
panic("implement me")
}
func (ds *GHOSTDAGDataStoreImpl) Commit(dbTx model.DBTransaction) error { func (ds *GHOSTDAGDataStoreImpl) Commit(dbTx model.DBTransaction) error {
panic("implement me") panic("implement me")
} }
@ -336,11 +332,15 @@ func (ds *GHOSTDAGDataStoreImpl) Get(dbContext model.DBReader, stagingArea *mode
return nil, nil return nil, nil
} }
func (ds *GHOSTDAGDataStoreImpl) UnstageAll(stagingArea *model.StagingArea) {
panic("implement me")
}
type DAGTopologyManagerImpl struct { type DAGTopologyManagerImpl struct {
parentsMap map[externalapi.DomainHash][]*externalapi.DomainHash parentsMap map[externalapi.DomainHash][]*externalapi.DomainHash
} }
func (dt *DAGTopologyManagerImpl) ChildInSelectedParentChainOf(stagingArea *model.StagingArea, context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) { func (dt *DAGTopologyManagerImpl) ChildInSelectedParentChainOf(stagingArea *model.StagingArea, lowHash, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
panic("implement me") panic("implement me")
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtopologymanager" "github.com/kaspanet/kaspad/domain/consensus/processes/dagtopologymanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager" "github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/reachabilitymanager" "github.com/kaspanet/kaspad/domain/consensus/processes/reachabilitymanager"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors" "github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
@ -26,16 +27,19 @@ type pruningProofManager struct {
dagTopologyManagers []model.DAGTopologyManager dagTopologyManagers []model.DAGTopologyManager
ghostdagManagers []model.GHOSTDAGManager ghostdagManagers []model.GHOSTDAGManager
reachabilityManagers []model.ReachabilityManager reachabilityManager model.ReachabilityManager
dagTraversalManagers []model.DAGTraversalManager dagTraversalManagers []model.DAGTraversalManager
parentsManager model.ParentsManager parentsManager model.ParentsManager
pruningManager model.PruningManager
ghostdagDataStores []model.GHOSTDAGDataStore ghostdagDataStores []model.GHOSTDAGDataStore
pruningStore model.PruningStore pruningStore model.PruningStore
blockHeaderStore model.BlockHeaderStore blockHeaderStore model.BlockHeaderStore
blockStatusStore model.BlockStatusStore blockStatusStore model.BlockStatusStore
finalityStore model.FinalityStore finalityStore model.FinalityStore
consensusStateStore model.ConsensusStateStore consensusStateStore model.ConsensusStateStore
blockRelationStore model.BlockRelationStore
reachabilityDataStore model.ReachabilityDataStore
genesisHash *externalapi.DomainHash genesisHash *externalapi.DomainHash
k externalapi.KType k externalapi.KType
@ -52,9 +56,10 @@ func New(
dagTopologyManagers []model.DAGTopologyManager, dagTopologyManagers []model.DAGTopologyManager,
ghostdagManagers []model.GHOSTDAGManager, ghostdagManagers []model.GHOSTDAGManager,
reachabilityManagers []model.ReachabilityManager, reachabilityManager model.ReachabilityManager,
dagTraversalManagers []model.DAGTraversalManager, dagTraversalManagers []model.DAGTraversalManager,
parentsManager model.ParentsManager, parentsManager model.ParentsManager,
pruningManager model.PruningManager,
ghostdagDataStores []model.GHOSTDAGDataStore, ghostdagDataStores []model.GHOSTDAGDataStore,
pruningStore model.PruningStore, pruningStore model.PruningStore,
@ -62,6 +67,8 @@ func New(
blockStatusStore model.BlockStatusStore, blockStatusStore model.BlockStatusStore,
finalityStore model.FinalityStore, finalityStore model.FinalityStore,
consensusStateStore model.ConsensusStateStore, consensusStateStore model.ConsensusStateStore,
blockRelationStore model.BlockRelationStore,
reachabilityDataStore model.ReachabilityDataStore,
genesisHash *externalapi.DomainHash, genesisHash *externalapi.DomainHash,
k externalapi.KType, k externalapi.KType,
@ -73,16 +80,19 @@ func New(
databaseContext: databaseContext, databaseContext: databaseContext,
dagTopologyManagers: dagTopologyManagers, dagTopologyManagers: dagTopologyManagers,
ghostdagManagers: ghostdagManagers, ghostdagManagers: ghostdagManagers,
reachabilityManagers: reachabilityManagers, reachabilityManager: reachabilityManager,
dagTraversalManagers: dagTraversalManagers, dagTraversalManagers: dagTraversalManagers,
parentsManager: parentsManager, parentsManager: parentsManager,
pruningManager: pruningManager,
ghostdagDataStores: ghostdagDataStores, ghostdagDataStores: ghostdagDataStores,
pruningStore: pruningStore, pruningStore: pruningStore,
blockHeaderStore: blockHeaderStore, blockHeaderStore: blockHeaderStore,
blockStatusStore: blockStatusStore, blockStatusStore: blockStatusStore,
finalityStore: finalityStore, finalityStore: finalityStore,
consensusStateStore: consensusStateStore, consensusStateStore: consensusStateStore,
blockRelationStore: blockRelationStore,
reachabilityDataStore: reachabilityDataStore,
genesisHash: genesisHash, genesisHash: genesisHash,
k: k, k: k,
@ -611,6 +621,209 @@ func (ppm *pruningProofManager) dagProcesses(
return reachabilityManagers, dagTopologyManagers, ghostdagManagers return reachabilityManagers, dagTopologyManagers, ghostdagManagers
} }
func (ppm *pruningProofManager) ghostdagDataWithoutPrunedBlocks(stagingArea *model.StagingArea, targetReachabilityDataStore model.ReachabilityDataStore,
data *externalapi.BlockGHOSTDAGData) (*externalapi.BlockGHOSTDAGData, bool, error) {
changed := false
mergeSetBlues := make([]*externalapi.DomainHash, 0, len(data.MergeSetBlues()))
for _, blockHash := range data.MergeSetBlues() {
hasReachabilityData, err := targetReachabilityDataStore.HasReachabilityData(ppm.databaseContext, stagingArea, blockHash)
if err != nil {
return nil, false, err
}
if !hasReachabilityData {
changed = true
if data.SelectedParent().Equal(blockHash) {
mergeSetBlues = append(mergeSetBlues, model.VirtualGenesisBlockHash)
}
continue
}
mergeSetBlues = append(mergeSetBlues, blockHash)
}
mergeSetReds := make([]*externalapi.DomainHash, 0, len(data.MergeSetReds()))
for _, blockHash := range data.MergeSetReds() {
hasReachabilityData, err := targetReachabilityDataStore.HasReachabilityData(ppm.databaseContext, stagingArea, blockHash)
if err != nil {
return nil, false, err
}
if !hasReachabilityData {
changed = true
continue
}
mergeSetReds = append(mergeSetReds, blockHash)
}
selectedParent := data.SelectedParent()
hasReachabilityData, err := targetReachabilityDataStore.HasReachabilityData(ppm.databaseContext, stagingArea, data.SelectedParent())
if err != nil {
return nil, false, err
}
if !hasReachabilityData {
changed = true
selectedParent = model.VirtualGenesisBlockHash
}
return externalapi.NewBlockGHOSTDAGData(
data.BlueScore(),
data.BlueWork(),
selectedParent,
mergeSetBlues,
mergeSetReds,
data.BluesAnticoneSizes(),
), changed, nil
}
func (ppm *pruningProofManager) populateProofReachabilityAndHeaders(pruningPointProof *externalapi.PruningPointProof,
targetReachabilityDataStore model.ReachabilityDataStore) error {
// We build a DAG of all multi-level relations between blocks in the proof. We make a upHeap of all blocks, so we can iterate
// over them in a topological way, and then build a DAG where we use all multi-level parents of a block to create edges, except
// parents that are already in the past of another parent (This can happen between two levels). We run GHOSTDAG on each block of
// that DAG, because GHOSTDAG is a requirement to calculate reachability. We then dismiss the GHOSTDAG data because it's not related
// to the GHOSTDAG data of the real DAG, and was used only for reachability.
// We need two staging areas: stagingArea which is used to commit the reachability data, and tmpStagingArea for the GHOSTDAG data
// of allProofBlocksUpHeap. The reason we need two areas is that we use the real GHOSTDAG data in order to order the heap in a topological
// way, and fake GHOSTDAG data for calculating reachability.
stagingArea := model.NewStagingArea()
tmpStagingArea := model.NewStagingArea()
bucket := consensusDB.MakeBucket([]byte("TMP"))
ghostdagDataStoreForTargetReachabilityManager := ghostdagdatastore.New(bucket, 0, false)
ghostdagDataStoreForTargetReachabilityManager.Stage(stagingArea, model.VirtualGenesisBlockHash, externalapi.NewBlockGHOSTDAGData(
0,
big.NewInt(0),
nil,
nil,
nil,
nil,
), false)
targetReachabilityManager := reachabilitymanager.New(ppm.databaseContext, ghostdagDataStoreForTargetReachabilityManager, targetReachabilityDataStore)
blockRelationStoreForTargetReachabilityManager := blockrelationstore.New(bucket, 0, false)
dagTopologyManagerForTargetReachabilityManager := dagtopologymanager.New(ppm.databaseContext, targetReachabilityManager, blockRelationStoreForTargetReachabilityManager, nil)
ghostdagManagerForTargetReachabilityManager := ghostdagmanager.New(ppm.databaseContext, dagTopologyManagerForTargetReachabilityManager, ghostdagDataStoreForTargetReachabilityManager, ppm.blockHeaderStore, 0, nil)
err := dagTopologyManagerForTargetReachabilityManager.SetParents(stagingArea, model.VirtualGenesisBlockHash, nil)
if err != nil {
return err
}
dagTopologyManager := dagtopologymanager.New(ppm.databaseContext, targetReachabilityManager, nil, nil)
ghostdagDataStore := ghostdagdatastore.New(bucket, 0, false)
tmpGHOSTDAGManager := ghostdagmanager.New(ppm.databaseContext, nil, ghostdagDataStore, nil, 0, nil)
dagTraversalManager := dagtraversalmanager.New(ppm.databaseContext, nil, ghostdagDataStore, nil, tmpGHOSTDAGManager, nil, nil, nil, 0)
allProofBlocksUpHeap := dagTraversalManager.NewUpHeap(tmpStagingArea)
dag := make(map[externalapi.DomainHash]struct {
parents hashset.HashSet
header externalapi.BlockHeader
})
for _, headers := range pruningPointProof.Headers {
for _, header := range headers {
blockHash := consensushashing.HeaderHash(header)
if _, ok := dag[*blockHash]; ok {
continue
}
dag[*blockHash] = struct {
parents hashset.HashSet
header externalapi.BlockHeader
}{parents: hashset.New(), header: header}
for level := 0; level <= ppm.maxBlockLevel; level++ {
for _, parent := range ppm.parentsManager.ParentsAtLevel(header, level) {
parent := parent
dag[*blockHash].parents.Add(parent)
}
}
// We stage temporary GHOSTDAG data that is needed in order to sort allProofBlocksUpHeap.
ghostdagDataStore.Stage(tmpStagingArea, blockHash, externalapi.NewBlockGHOSTDAGData(header.BlueScore(), header.BlueWork(), nil, nil, nil, nil), false)
err := allProofBlocksUpHeap.Push(blockHash)
if err != nil {
return err
}
}
}
var selectedTip *externalapi.DomainHash
for allProofBlocksUpHeap.Len() > 0 {
blockHash := allProofBlocksUpHeap.Pop()
block := dag[*blockHash]
parentsHeap := dagTraversalManager.NewDownHeap(tmpStagingArea)
for parent := range block.parents {
parent := parent
if _, ok := dag[parent]; !ok {
continue
}
err := parentsHeap.Push(&parent)
if err != nil {
return err
}
}
fakeParents := []*externalapi.DomainHash{}
for parentsHeap.Len() > 0 {
parent := parentsHeap.Pop()
isAncestorOfAny, err := dagTopologyManager.IsAncestorOfAny(stagingArea, parent, fakeParents)
if err != nil {
return err
}
if isAncestorOfAny {
continue
}
fakeParents = append(fakeParents, parent)
}
if len(fakeParents) == 0 {
fakeParents = append(fakeParents, model.VirtualGenesisBlockHash)
}
err := dagTopologyManagerForTargetReachabilityManager.SetParents(stagingArea, blockHash, fakeParents)
if err != nil {
return err
}
err = ghostdagManagerForTargetReachabilityManager.GHOSTDAG(stagingArea, blockHash)
if err != nil {
return err
}
err = targetReachabilityManager.AddBlock(stagingArea, blockHash)
if err != nil {
return err
}
if selectedTip == nil {
selectedTip = blockHash
} else {
selectedTip, err = ghostdagManagerForTargetReachabilityManager.ChooseSelectedParent(stagingArea, selectedTip, blockHash)
if err != nil {
return err
}
}
if selectedTip.Equal(blockHash) {
err := targetReachabilityManager.UpdateReindexRoot(stagingArea, selectedTip)
if err != nil {
return err
}
}
}
ghostdagDataStoreForTargetReachabilityManager.UnstageAll(stagingArea)
blockRelationStoreForTargetReachabilityManager.UnstageAll(stagingArea)
err = staging.CommitAllChanges(ppm.databaseContext, stagingArea)
if err != nil {
return err
}
return nil
}
// ApplyPruningPointProof applies the given pruning proof to the current consensus. Specifically, // ApplyPruningPointProof applies the given pruning proof to the current consensus. Specifically,
// it's meant to be used against the StagingConsensus during headers-proof IBD. Note that for // it's meant to be used against the StagingConsensus during headers-proof IBD. Note that for
// performance reasons this operation is NOT atomic. If the process fails for whatever reason // performance reasons this operation is NOT atomic. If the process fails for whatever reason
@ -619,9 +832,25 @@ func (ppm *pruningProofManager) ApplyPruningPointProof(pruningPointProof *extern
onEnd := logger.LogAndMeasureExecutionTime(log, "ApplyPruningPointProof") onEnd := logger.LogAndMeasureExecutionTime(log, "ApplyPruningPointProof")
defer onEnd() defer onEnd()
stagingArea := model.NewStagingArea()
for _, headers := range pruningPointProof.Headers {
for _, header := range headers {
blockHash := consensushashing.HeaderHash(header)
ppm.blockHeaderStore.Stage(stagingArea, blockHash, header)
}
}
err := staging.CommitAllChanges(ppm.databaseContext, stagingArea)
if err != nil {
return err
}
err = ppm.populateProofReachabilityAndHeaders(pruningPointProof, ppm.reachabilityDataStore)
if err != nil {
return err
}
for blockLevel, headers := range pruningPointProof.Headers { for blockLevel, headers := range pruningPointProof.Headers {
log.Infof("Applying level %d from the pruning point proof", blockLevel) log.Infof("Applying level %d from the pruning point proof", blockLevel)
var selectedTip *externalapi.DomainHash
for i, header := range headers { for i, header := range headers {
if i%1000 == 0 { if i%1000 == 0 {
log.Infof("Applying level %d from the pruning point proof - applied %d headers out of %d", blockLevel, i, len(headers)) log.Infof("Applying level %d from the pruning point proof - applied %d headers out of %d", blockLevel, i, len(headers))
@ -687,27 +916,6 @@ func (ppm *pruningProofManager) ApplyPruningPointProof(pruningPointProof *extern
ppm.blockStatusStore.Stage(stagingArea, blockHash, externalapi.StatusHeaderOnly) ppm.blockStatusStore.Stage(stagingArea, blockHash, externalapi.StatusHeaderOnly)
} }
if selectedTip == nil {
selectedTip = blockHash
} else {
selectedTip, err = ppm.ghostdagManagers[blockLevel].ChooseSelectedParent(stagingArea, selectedTip, blockHash)
if err != nil {
return err
}
}
err = ppm.reachabilityManagers[blockLevel].AddBlock(stagingArea, blockHash)
if err != nil {
return err
}
if selectedTip.Equal(blockHash) {
err := ppm.reachabilityManagers[blockLevel].UpdateReindexRoot(stagingArea, selectedTip)
if err != nil {
return err
}
}
err = staging.CommitAllChanges(ppm.databaseContext, stagingArea) err = staging.CommitAllChanges(ppm.databaseContext, stagingArea)
if err != nil { if err != nil {
return err return err
@ -718,7 +926,7 @@ func (ppm *pruningProofManager) ApplyPruningPointProof(pruningPointProof *extern
pruningPointHeader := pruningPointProof.Headers[0][len(pruningPointProof.Headers[0])-1] pruningPointHeader := pruningPointProof.Headers[0][len(pruningPointProof.Headers[0])-1]
pruningPoint := consensushashing.HeaderHash(pruningPointHeader) pruningPoint := consensushashing.HeaderHash(pruningPointHeader)
stagingArea := model.NewStagingArea() stagingArea = model.NewStagingArea()
ppm.consensusStateStore.StageTips(stagingArea, []*externalapi.DomainHash{pruningPoint}) ppm.consensusStateStore.StageTips(stagingArea, []*externalapi.DomainHash{pruningPoint})
return staging.CommitAllChanges(ppm.databaseContext, stagingArea) return staging.CommitAllChanges(ppm.databaseContext, stagingArea)
} }

View File

@ -16,7 +16,7 @@ type reachabilityDataStoreMock struct {
reachabilityReindexRootStaging *externalapi.DomainHash reachabilityReindexRootStaging *externalapi.DomainHash
} }
func (r *reachabilityDataStoreMock) Discard() { func (r *reachabilityDataStoreMock) Delete(_ model.DBWriter) error {
panic("implement me") panic("implement me")
} }

View File

@ -152,6 +152,10 @@ func (rt *reachabilityManager) IsReachabilityTreeAncestorOf(stagingArea *model.S
func (rt *reachabilityManager) FindNextAncestor(stagingArea *model.StagingArea, func (rt *reachabilityManager) FindNextAncestor(stagingArea *model.StagingArea,
descendant, ancestor *externalapi.DomainHash) (*externalapi.DomainHash, error) { descendant, ancestor *externalapi.DomainHash) (*externalapi.DomainHash, error) {
if ancestor.Equal(descendant) {
return nil, errors.Errorf("ancestor is equal to descendant")
}
childrenOfAncestor, err := rt.children(stagingArea, ancestor) childrenOfAncestor, err := rt.children(stagingArea, ancestor)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -60,7 +60,7 @@ func (tc *testConsensus) PruningStore() model.PruningStore {
} }
func (tc *testConsensus) ReachabilityDataStore() model.ReachabilityDataStore { func (tc *testConsensus) ReachabilityDataStore() model.ReachabilityDataStore {
return tc.reachabilityDataStores[0] return tc.reachabilityDataStore
} }
func (tc *testConsensus) UTXODiffStore() model.UTXODiffStore { func (tc *testConsensus) UTXODiffStore() model.UTXODiffStore {

View File

@ -57,6 +57,7 @@ func (rd *reachabilityData) FutureCoveringSet() model.FutureCoveringTreeNodeSet
} }
func (rd *reachabilityData) CloneMutable() model.MutableReachabilityData { func (rd *reachabilityData) CloneMutable() model.MutableReachabilityData {
//return rd
return &reachabilityData{ return &reachabilityData{
children: externalapi.CloneHashes(rd.children), children: externalapi.CloneHashes(rd.children),
parent: rd.parent, parent: rd.parent,

View File

@ -82,11 +82,15 @@ func (d *domain) InitStagingConsensus() error {
cfg := *d.consensusConfig cfg := *d.consensusConfig
cfg.SkipAddingGenesis = true cfg.SkipAddingGenesis = true
consensusInstance, err := consensusFactory.NewConsensus(&cfg, d.db, inactivePrefix) consensusInstance, shouldMigrate, err := consensusFactory.NewConsensus(&cfg, d.db, inactivePrefix)
if err != nil { if err != nil {
return err return err
} }
if shouldMigrate {
return errors.Errorf("A fresh consensus should never return shouldMigrate=true")
}
d.stagingConsensus = &consensusInstance d.stagingConsensus = &consensusInstance
return nil return nil
} }
@ -183,7 +187,7 @@ func New(consensusConfig *consensus.Config, mempoolConfig *mempool.Config, db in
} }
consensusFactory := consensus.NewFactory() consensusFactory := consensus.NewFactory()
consensusInstance, err := consensusFactory.NewConsensus(consensusConfig, db, activePrefix) consensusInstance, shouldMigrate, err := consensusFactory.NewConsensus(consensusConfig, db, activePrefix)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -194,6 +198,13 @@ func New(consensusConfig *consensus.Config, mempoolConfig *mempool.Config, db in
db: db, db: db,
} }
if shouldMigrate {
err := domainInstance.migrate()
if err != nil {
return nil, err
}
}
miningManagerFactory := miningmanager.NewFactory() miningManagerFactory := miningmanager.NewFactory()
// We create a consensus wrapper because the actual consensus might change // We create a consensus wrapper because the actual consensus might change

7
domain/log.go Normal file
View File

@ -0,0 +1,7 @@
package domain
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
)
var log = logger.RegisterSubSystem("DOMN")

230
domain/migrate.go Normal file
View File

@ -0,0 +1,230 @@
package domain
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"math"
)
func (d *domain) migrate() error {
log.Infof("Starting migration")
err := d.InitStagingConsensus()
if err != nil {
return err
}
err = syncConsensuses(d.Consensus(), d.StagingConsensus())
if err != nil {
return err
}
err = d.CommitStagingConsensus()
if err != nil {
return err
}
log.Info("Done migrating")
return nil
}
func syncConsensuses(syncer, syncee externalapi.Consensus) error {
pruningPointProof, err := syncer.BuildPruningPointProof()
if err != nil {
return err
}
err = syncee.ApplyPruningPointProof(pruningPointProof)
if err != nil {
return err
}
pruningPointHeaders, err := syncer.PruningPointHeaders()
if err != nil {
return err
}
err = syncee.ImportPruningPoints(pruningPointHeaders)
if err != nil {
return err
}
pruningPointAndItsAnticone, err := syncer.PruningPointAndItsAnticone()
if err != nil {
return err
}
for _, blockHash := range pruningPointAndItsAnticone {
block, err := syncer.GetBlock(blockHash)
if err != nil {
return err
}
blockDAAWindowHashes, err := syncer.BlockDAAWindowHashes(blockHash)
if err != nil {
return err
}
ghostdagDataBlockHashes, err := syncer.TrustedBlockAssociatedGHOSTDAGDataBlockHashes(blockHash)
if err != nil {
return err
}
blockWithTrustedData := &externalapi.BlockWithTrustedData{
Block: block,
DAAWindow: make([]*externalapi.TrustedDataDataDAAHeader, 0, len(blockDAAWindowHashes)),
GHOSTDAGData: make([]*externalapi.BlockGHOSTDAGDataHashPair, 0, len(ghostdagDataBlockHashes)),
}
for i, daaBlockHash := range blockDAAWindowHashes {
trustedDataDataDAAHeader, err := syncer.TrustedDataDataDAAHeader(blockHash, daaBlockHash, uint64(i))
if err != nil {
return err
}
blockWithTrustedData.DAAWindow = append(blockWithTrustedData.DAAWindow, trustedDataDataDAAHeader)
}
for _, ghostdagDataBlockHash := range ghostdagDataBlockHashes {
data, err := syncer.TrustedGHOSTDAGData(ghostdagDataBlockHash)
if err != nil {
return err
}
blockWithTrustedData.GHOSTDAGData = append(blockWithTrustedData.GHOSTDAGData, &externalapi.BlockGHOSTDAGDataHashPair{
Hash: ghostdagDataBlockHash,
GHOSTDAGData: data,
})
}
_, err = syncee.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)
if err != nil {
return err
}
}
syncerVirtualSelectedParent, err := syncer.GetVirtualSelectedParent()
if err != nil {
return err
}
pruningPoint, err := syncer.PruningPoint()
if err != nil {
return err
}
missingBlocks, _, err := syncer.GetHashesBetween(pruningPoint, syncerVirtualSelectedParent, math.MaxUint64)
if err != nil {
return err
}
syncerTips, err := syncer.Tips()
if err != nil {
return err
}
for _, tip := range syncerTips {
if tip.Equal(syncerVirtualSelectedParent) {
continue
}
anticone, err := syncer.GetAnticone(syncerVirtualSelectedParent, tip, 0)
if err != nil {
return err
}
missingBlocks = append(missingBlocks, anticone...)
}
percents := 0
for i, blocksHash := range missingBlocks {
blockInfo, err := syncee.GetBlockInfo(blocksHash)
if err != nil {
return err
}
if blockInfo.Exists {
continue
}
block, err := syncer.GetBlock(blocksHash)
if err != nil {
return err
}
_, err = syncee.ValidateAndInsertBlock(block, false)
if err != nil {
return err
}
newPercents := 100 * i / len(missingBlocks)
if newPercents > percents {
percents = newPercents
log.Infof("Processed %d%% of the blocks", 100*i/len(missingBlocks))
}
}
var fromOutpoint *externalapi.DomainOutpoint
const step = 100_000
for {
outpointAndUTXOEntryPairs, err := syncer.GetPruningPointUTXOs(pruningPoint, fromOutpoint, step)
if err != nil {
return err
}
fromOutpoint = outpointAndUTXOEntryPairs[len(outpointAndUTXOEntryPairs)-1].Outpoint
err = syncee.AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs)
if err != nil {
return err
}
if len(outpointAndUTXOEntryPairs) < step {
break
}
}
// Check that ValidateAndInsertImportedPruningPoint works given the right arguments.
err = syncee.ValidateAndInsertImportedPruningPoint(pruningPoint)
if err != nil {
return err
}
emptyCoinbase := &externalapi.DomainCoinbaseData{
ScriptPublicKey: &externalapi.ScriptPublicKey{
Script: nil,
Version: 0,
},
}
// Check that we can build a block just after importing the pruning point.
_, err = syncee.BuildBlock(emptyCoinbase, nil)
if err != nil {
return err
}
estimatedVirtualDAAScoreTarget, err := syncer.GetVirtualDAAScore()
if err != nil {
return err
}
virtualDAAScoreStart := uint64(0)
percents = 0
for i := 0; ; i++ {
if i%10 == 0 {
virtualDAAScore, err := syncee.GetVirtualDAAScore()
if err != nil {
return err
}
newPercents := int(float64(virtualDAAScore-virtualDAAScoreStart) / float64(estimatedVirtualDAAScoreTarget-virtualDAAScoreStart) * 100)
if newPercents > percents {
percents = newPercents
log.Infof("Resolving virtual. Estimated progress: %d%%", percents)
}
}
_, isCompletelyResolved, err := syncee.ResolveVirtual()
if err != nil {
return err
}
if isCompletelyResolved {
log.Infof("Resolved virtual")
break
}
}
return nil
}