Repository: kaspanet/kaspad
Pull request #1190 (2020-12-07 13:39:51)

[NOD-1590] Implement optimized finalityPoint calculation mechanism

ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master

Patch:
diff --git a/domain/consensus/consensus.go b/domain/consensus/consensus.go
index 4d16e5a3..7e49e8ab 100644
--- a/domain/consensus/consensus.go
+++ b/domain/consensus/consensus.go
@@ -29,6 +29,7 @@ type consensus struct {
 	mergeDepthManager     model.MergeDepthManager
 	pruningManager        model.PruningManager
 	reachabilityManager   model.ReachabilityManager
+	finalityManager       model.FinalityManager
 
 	acceptanceDataStore   model.AcceptanceDataStore
 	blockStore            model.BlockStore
@@ -42,6 +43,7 @@ type consensus struct {
 	multisetStore         model.MultisetStore
 	reachabilityDataStore model.ReachabilityDataStore
 	utxoDiffStore         model.UTXODiffStore
+	finalityStore         model.FinalityStore
 }
 
 // BuildBlock builds a block over the current state, with the transactions
diff --git a/domain/consensus/datastructures/finalitystore/finality_store.go b/domain/consensus/datastructures/finalitystore/finality_store.go
new file mode 100644
index 00000000..4cd34e35
--- /dev/null
+++ b/domain/consensus/datastructures/finalitystore/finality_store.go
@@ -0,0 +1,78 @@
+package finalitystore
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
+)
+
+var bucket = dbkeys.MakeBucket([]byte("finality-points"))
+
+type finalityStore struct {
+	staging  map[externalapi.DomainHash]*externalapi.DomainHash
+	toDelete map[externalapi.DomainHash]struct{}
+	cache    *lrucache.LRUCache
+}
+
+// New instantiates a new FinalityStore
+func New(cacheSize int) model.FinalityStore {
+	return &finalityStore{
+		staging:  make(map[externalapi.DomainHash]*externalapi.DomainHash),
+		toDelete: make(map[externalapi.DomainHash]struct{}),
+		cache:    lrucache.New(cacheSize),
+	}
+}
+
+func (fs *finalityStore) StageFinalityPoint(blockHash *externalapi.DomainHash, finalityPointHash *externalapi.DomainHash) {
+	fs.staging[*blockHash] = finalityPointHash
+}
+
+func (fs *finalityStore) FinalityPoint(
+	dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
+	if finalityPointHash, ok := fs.staging[*blockHash]; ok {
+		return finalityPointHash, nil
+	}
+
+	if finalityPointHash, ok := fs.cache.Get(blockHash); ok {
+		return finalityPointHash.(*externalapi.DomainHash), nil
+	}
+
+	finalityPointHashBytes, err := dbContext.Get(fs.hashAsKey(blockHash))
+	if err != nil {
+		return nil, err
+	}
+	finalityPointHash, err := hashes.FromBytes(finalityPointHashBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	fs.cache.Add(blockHash, finalityPointHash)
+	return finalityPointHash, nil
+}
+
+func (fs *finalityStore) Discard() {
+	fs.staging = make(map[externalapi.DomainHash]*externalapi.DomainHash)
+}
+
+func (fs *finalityStore) Commit(dbTx model.DBTransaction) error {
+	for hash, finalityPointHash := range fs.staging {
+		err := dbTx.Put(fs.hashAsKey(&hash), finalityPointHash[:])
+		if err != nil {
+			return err
+		}
+		fs.cache.Add(&hash, finalityPointHash)
+	}
+
+	fs.Discard()
+	return nil
+}
+
+func (fs *finalityStore) IsStaged() bool {
+	return len(fs.staging) == 0
+}
+
+func (fs *finalityStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
+	return bucket.Key(hash[:])
+}
diff --git a/domain/consensus/factory.go b/domain/consensus/factory.go
index ddcd183b..ee8f9341 100644
--- a/domain/consensus/factory.go
+++ b/domain/consensus/factory.go
@@ -1,12 +1,12 @@
 package consensus
 
 import (
-	"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
 	"io/ioutil"
 	"os"
 	"sync"
 
-	"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
+	"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
+	"github.com/kaspanet/kaspad/domain/consensus/processes/finalitymanager"
 
 	consensusdatabase "github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/datastructures/acceptancedatastore"
@@ -15,6 +15,7 @@ import (
 	"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockstatusstore"
 	"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockstore"
 	"github.com/kaspanet/kaspad/domain/consensus/datastructures/consensusstatestore"
+	"github.com/kaspanet/kaspad/domain/consensus/datastructures/finalitystore"
 	"github.com/kaspanet/kaspad/domain/consensus/datastructures/ghostdagdatastore"
 	"github.com/kaspanet/kaspad/domain/consensus/datastructures/headertipsstore"
 	"github.com/kaspanet/kaspad/domain/consensus/datastructures/multisetstore"
@@ -40,6 +41,7 @@ import (
 	"github.com/kaspanet/kaspad/domain/consensus/processes/transactionvalidator"
 	"github.com/kaspanet/kaspad/domain/dagconfig"
 	infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
+	"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
 )
 
 // Factory instantiates new Consensuses
@@ -80,6 +82,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 	consensusStateStore := consensusstatestore.New()
 	ghostdagDataStore := ghostdagdatastore.New(10_000)
 	headerTipsStore := headertipsstore.New()
+	finalityStore := finalitystore.New(200)
 
 	// Processes
 	reachabilityManager := reachabilitymanager.New(
@@ -89,7 +92,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 	dagTopologyManager := dagtopologymanager.New(
 		dbManager,
 		reachabilityManager,
-		blockRelationStore)
+		blockRelationStore,
+		ghostdagDataStore)
 	ghostdagManager := ghostdagmanager.New(
 		dbManager,
 		dagTopologyManager,
@@ -137,11 +141,18 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 		acceptanceDataStore)
 	headerTipsManager := headertipsmanager.New(dbManager, dagTopologyManager, ghostdagManager, headerTipsStore)
 	genesisHash := dagParams.GenesisHash
+	finalityManager := finalitymanager.New(
+		dbManager,
+		dagTopologyManager,
+		finalityStore,
+		ghostdagDataStore,
+		genesisHash,
+		dagParams.FinalityDepth())
 	mergeDepthManager := mergedepthmanager.New(
-		dagParams.FinalityDepth(),
 		dbManager,
 		dagTopologyManager,
 		dagTraversalManager,
+		finalityManager,
 		ghostdagDataStore)
 	blockValidator := blockvalidator.New(
 		dagParams.PowMax,
@@ -163,16 +174,17 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 		dagTraversalManager,
 		coinbaseManager,
 		mergeDepthManager,
-		pruningStore,
+		reachabilityManager,
 
+		pruningStore,
 		blockStore,
 		ghostdagDataStore,
 		blockHeaderStore,
 		blockStatusStore,
+		reachabilityDataStore,
 	)
 	consensusStateManager, err := consensusstatemanager.New(
 		dbManager,
-		dagParams.FinalityDepth(),
 		dagParams.PruningDepth(),
 		dagParams.MaxMassAcceptedByBlock,
 		dagParams.MaxBlockParents,
@@ -188,6 +200,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 		reachabilityManager,
 		coinbaseManager,
 		mergeDepthManager,
+		finalityManager,
 
 		blockStatusStore,
 		ghostdagDataStore,
@@ -274,7 +287,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 		reachabilityDataStore,
 		utxoDiffStore,
 		blockHeaderStore,
-		headerTipsStore)
+		headerTipsStore,
+		finalityStore)
 
 	c := &consensus{
 		lock:            &sync.Mutex{},
@@ -296,6 +310,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 		mergeDepthManager:     mergeDepthManager,
 		pruningManager:        pruningManager,
 		reachabilityManager:   reachabilityManager,
+		finalityManager:       finalityManager,
 
 		acceptanceDataStore:   acceptanceDataStore,
 		blockStore:            blockStore,
@@ -309,6 +324,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 		multisetStore:         multisetStore,
 		reachabilityDataStore: reachabilityDataStore,
 		utxoDiffStore:         utxoDiffStore,
+		finalityStore:         finalityStore,
 	}
 
 	genesisInfo, err := c.GetBlockInfo(genesisHash)
diff --git a/domain/consensus/model/interface_datastructures_finalitystore.go b/domain/consensus/model/interface_datastructures_finalitystore.go
new file mode 100644
index 00000000..52b11b3d
--- /dev/null
+++ b/domain/consensus/model/interface_datastructures_finalitystore.go
@@ -0,0 +1,13 @@
+package model
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+)
+
+// FinalityStore represents a store for finality data
+type FinalityStore interface {
+	Store
+	IsStaged() bool
+	StageFinalityPoint(blockHash *externalapi.DomainHash, finalityPointHash *externalapi.DomainHash)
+	FinalityPoint(dbContext DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
+}
diff --git a/domain/consensus/model/interface_processes_dagtopologymanager.go b/domain/consensus/model/interface_processes_dagtopologymanager.go
index 7e93e89e..bb49f792 100644
--- a/domain/consensus/model/interface_processes_dagtopologymanager.go
+++ b/domain/consensus/model/interface_processes_dagtopologymanager.go
@@ -13,6 +13,7 @@ type DAGTopologyManager interface {
 	IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
 	IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error)
 	IsInSelectedParentChainOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
+	ChildInSelectedParentChainOf(context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
 
 	SetParents(blockHash *externalapi.DomainHash, parentHashes []*externalapi.DomainHash) error
 }
diff --git a/domain/consensus/model/interface_processes_finalitymanager.go b/domain/consensus/model/interface_processes_finalitymanager.go
new file mode 100644
index 00000000..c58a5c33
--- /dev/null
+++ b/domain/consensus/model/interface_processes_finalitymanager.go
@@ -0,0 +1,10 @@
+package model
+
+import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+
+// FinalityManager provides method to validate that a block does not violate finality
+type FinalityManager interface {
+	IsViolatingFinality(blockHash *externalapi.DomainHash) (bool, error)
+	VirtualFinalityPoint() (*externalapi.DomainHash, error)
+	FinalityPoint(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
+}
diff --git a/domain/consensus/model/interface_processes_reachabilitytree.go b/domain/consensus/model/interface_processes_reachabilitytree.go
index 47284dae..e2bbbd7f 100644
--- a/domain/consensus/model/interface_processes_reachabilitytree.go
+++ b/domain/consensus/model/interface_processes_reachabilitytree.go
@@ -9,4 +9,5 @@ type ReachabilityManager interface {
 	IsReachabilityTreeAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
 	IsDAGAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
 	UpdateReindexRoot(selectedTip *externalapi.DomainHash) error
+	FindAncestorOfThisAmongChildrenOfOther(this, other *externalapi.DomainHash) (*externalapi.DomainHash, error)
 }
diff --git a/domain/consensus/processes/blockprocessor/blockprocessor.go b/domain/consensus/processes/blockprocessor/blockprocessor.go
index 3acb04cf..e3e9ed46 100644
--- a/domain/consensus/processes/blockprocessor/blockprocessor.go
+++ b/domain/consensus/processes/blockprocessor/blockprocessor.go
@@ -36,6 +36,7 @@ type blockProcessor struct {
 	utxoDiffStore         model.UTXODiffStore
 	blockHeaderStore      model.BlockHeaderStore
 	headerTipsStore       model.HeaderTipsStore
+	finalityStore         model.FinalityStore
 
 	stores []model.Store
 }
@@ -67,7 +68,9 @@ func New(
 	reachabilityDataStore model.ReachabilityDataStore,
 	utxoDiffStore model.UTXODiffStore,
 	blockHeaderStore model.BlockHeaderStore,
-	headerTipsStore model.HeaderTipsStore) model.BlockProcessor {
+	headerTipsStore model.HeaderTipsStore,
+	finalityStore model.FinalityStore,
+) model.BlockProcessor {
 
 	return &blockProcessor{
 		genesisHash:           genesisHash,
@@ -96,6 +99,7 @@ func New(
 		utxoDiffStore:         utxoDiffStore,
 		blockHeaderStore:      blockHeaderStore,
 		headerTipsStore:       headerTipsStore,
+		finalityStore:         finalityStore,
 
 		stores: []model.Store{
 			consensusStateStore,
@@ -111,6 +115,7 @@ func New(
 			utxoDiffStore,
 			blockHeaderStore,
 			headerTipsStore,
+			finalityStore,
 		},
 	}
 }
diff --git a/domain/consensus/processes/blockprocessor/validateandinsertblock.go b/domain/consensus/processes/blockprocessor/validateandinsertblock.go
index 35f84546..0cdff5ec 100644
--- a/domain/consensus/processes/blockprocessor/validateandinsertblock.go
+++ b/domain/consensus/processes/blockprocessor/validateandinsertblock.go
@@ -40,18 +40,6 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
 		return err
 	}
 
-	hasHeader, err := bp.hasHeader(blockHash)
-	if err != nil {
-		return err
-	}
-
-	if !hasHeader {
-		err = bp.reachabilityManager.AddBlock(blockHash)
-		if err != nil {
-			return err
-		}
-	}
-
 	if insertMode == insertModeHeader {
 		bp.blockStatusStore.Stage(blockHash, externalapi.StatusHeaderOnly)
 	} else {
diff --git a/domain/consensus/processes/blockvalidator/block_header_in_context.go b/domain/consensus/processes/blockvalidator/block_header_in_context.go
index cc2aa156..0d2f3fdf 100644
--- a/domain/consensus/processes/blockvalidator/block_header_in_context.go
+++ b/domain/consensus/processes/blockvalidator/block_header_in_context.go
@@ -37,6 +37,21 @@ func (v *blockValidator) ValidateHeaderInContext(blockHash *externalapi.DomainHa
 		return err
 	}
 
+	// If needed - calculate reachability data right before calling CheckBoundedMergeDepth,
+	// since it's used to find a block's finality point.
+	// This might not be required if this block's header has previously been received during
+	// headers-first synchronization.
+	hasReachabilityData, err := v.reachabilityStore.HasReachabilityData(v.databaseContext, blockHash)
+	if err != nil {
+		return err
+	}
+	if !hasReachabilityData {
+		err = v.reachabilityManager.AddBlock(blockHash)
+		if err != nil {
+			return err
+		}
+	}
+
 	err = v.mergeDepthManager.CheckBoundedMergeDepth(blockHash)
 	if err != nil {
 		return err
diff --git a/domain/consensus/processes/blockvalidator/blockvalidator.go b/domain/consensus/processes/blockvalidator/blockvalidator.go
index 09c5621a..bb5bf40a 100644
--- a/domain/consensus/processes/blockvalidator/blockvalidator.go
+++ b/domain/consensus/processes/blockvalidator/blockvalidator.go
@@ -33,11 +33,13 @@ type blockValidator struct {
 	coinbaseManager       model.CoinbaseManager
 	mergeDepthManager     model.MergeDepthManager
 	pruningStore          model.PruningStore
+	reachabilityManager   model.ReachabilityManager
 
 	blockStore        model.BlockStore
 	ghostdagDataStore model.GHOSTDAGDataStore
 	blockHeaderStore  model.BlockHeaderStore
 	blockStatusStore  model.BlockStatusStore
+	reachabilityStore model.ReachabilityDataStore
 }
 
 // New instantiates a new BlockValidator
@@ -61,22 +63,27 @@ func New(powMax *big.Int,
 	dagTraversalManager model.DAGTraversalManager,
 	coinbaseManager model.CoinbaseManager,
 	mergeDepthManager model.MergeDepthManager,
+	reachabilityManager model.ReachabilityManager,
+
 	pruningStore model.PruningStore,
 
 	blockStore model.BlockStore,
 	ghostdagDataStore model.GHOSTDAGDataStore,
 	blockHeaderStore model.BlockHeaderStore,
-	blockStatusStore model.BlockStatusStore) model.BlockValidator {
+	blockStatusStore model.BlockStatusStore,
+	reachabilityStore model.ReachabilityDataStore,
+) model.BlockValidator {
 
 	return &blockValidator{
-		powMax:                      powMax,
-		skipPoW:                     skipPoW,
-		genesisHash:                 genesisHash,
-		enableNonNativeSubnetworks:  enableNonNativeSubnetworks,
-		powMaxBits:                  util.BigToCompact(powMax),
-		maxBlockSize:                maxBlockSize,
-		mergeSetSizeLimit:           mergeSetSizeLimit,
-		maxBlockParents:             maxBlockParents,
+		powMax:                     powMax,
+		skipPoW:                    skipPoW,
+		genesisHash:                genesisHash,
+		enableNonNativeSubnetworks: enableNonNativeSubnetworks,
+		powMaxBits:                 util.BigToCompact(powMax),
+		maxBlockSize:               maxBlockSize,
+		mergeSetSizeLimit:          mergeSetSizeLimit,
+		maxBlockParents:            maxBlockParents,
+
 		timestampDeviationTolerance: timestampDeviationTolerance,
 		targetTimePerBlock:          targetTimePerBlock,
 		databaseContext:             databaseContext,
@@ -88,11 +95,13 @@ func New(powMax *big.Int,
 		dagTraversalManager:         dagTraversalManager,
 		coinbaseManager:             coinbaseManager,
 		mergeDepthManager:           mergeDepthManager,
-		pruningStore:                pruningStore,
+		reachabilityManager:         reachabilityManager,
 
+		pruningStore:      pruningStore,
 		blockStore:        blockStore,
 		ghostdagDataStore: ghostdagDataStore,
 		blockHeaderStore:  blockHeaderStore,
 		blockStatusStore:  blockStatusStore,
+		reachabilityStore: reachabilityStore,
 	}
 }
diff --git a/domain/consensus/processes/consensusstatemanager/check_finality_violation.go b/domain/consensus/processes/consensusstatemanager/check_finality_violation.go
new file mode 100644
index 00000000..c8c9e022
--- /dev/null
+++ b/domain/consensus/processes/consensusstatemanager/check_finality_violation.go
@@ -0,0 +1,24 @@
+package consensusstatemanager
+
+import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+
+func (csm *consensusStateManager) checkFinalityViolation(
+	blockHash *externalapi.DomainHash) error {
+
+	log.Tracef("checkFinalityViolation start for block %s", blockHash)
+	defer log.Tracef("checkFinalityViolation end for block %s", blockHash)
+
+	isViolatingFinality, err := csm.finalityManager.IsViolatingFinality(blockHash)
+	if err != nil {
+		return err
+	}
+
+	if isViolatingFinality {
+		csm.blockStatusStore.Stage(blockHash, externalapi.StatusUTXOPendingVerification)
+		log.Warnf("Finality Violation Detected! Block %s violates finality!", blockHash)
+		return nil
+	}
+	log.Tracef("Block %s does not violate finality", blockHash)
+
+	return nil
+}
diff --git a/domain/consensus/processes/consensusstatemanager/consensus_state_manager.go b/domain/consensus/processes/consensusstatemanager/consensus_state_manager.go
index 01cb3b32..c0c8ab73 100644
--- a/domain/consensus/processes/consensusstatemanager/consensus_state_manager.go
+++ b/domain/consensus/processes/consensusstatemanager/consensus_state_manager.go
@@ -7,7 +7,6 @@ import (
 
 // consensusStateManager manages the node's consensus state
 type consensusStateManager struct {
-	finalityDepth          uint64
 	pruningDepth           uint64
 	maxMassAcceptedByBlock uint64
 	maxBlockParents        model.KType
@@ -24,8 +23,9 @@ type consensusStateManager struct {
 	reachabilityManager   model.ReachabilityManager
 	coinbaseManager       model.CoinbaseManager
 	mergeDepthManager     model.MergeDepthManager
-	headerTipsStore       model.HeaderTipsStore
+	finalityManager       model.FinalityManager
 
+	headerTipsStore     model.HeaderTipsStore
 	blockStatusStore    model.BlockStatusStore
 	ghostdagDataStore   model.GHOSTDAGDataStore
 	consensusStateStore model.ConsensusStateStore
@@ -42,7 +42,6 @@ type consensusStateManager struct {
 // New instantiates a new ConsensusStateManager
 func New(
 	databaseContext model.DBManager,
-	finalityDepth uint64,
 	pruningDepth uint64,
 	maxMassAcceptedByBlock uint64,
 	maxBlockParents model.KType,
@@ -58,6 +57,7 @@ func New(
 	reachabilityManager model.ReachabilityManager,
 	coinbaseManager model.CoinbaseManager,
 	mergeDepthManager model.MergeDepthManager,
+	finalityManager model.FinalityManager,
 
 	blockStatusStore model.BlockStatusStore,
 	ghostdagDataStore model.GHOSTDAGDataStore,
@@ -71,7 +71,6 @@ func New(
 	headerTipsStore model.HeaderTipsStore) (model.ConsensusStateManager, error) {
 
 	csm := &consensusStateManager{
-		finalityDepth:          finalityDepth,
 		pruningDepth:           pruningDepth,
 		maxMassAcceptedByBlock: maxMassAcceptedByBlock,
 		maxBlockParents:        maxBlockParents,
@@ -88,6 +87,7 @@ func New(
 		reachabilityManager:   reachabilityManager,
 		coinbaseManager:       coinbaseManager,
 		mergeDepthManager:     mergeDepthManager,
+		finalityManager:       finalityManager,
 
 		multisetStore:       multisetStore,
 		blockStore:          blockStore,
diff --git a/domain/consensus/processes/consensusstatemanager/finality.go b/domain/consensus/processes/consensusstatemanager/finality.go
deleted file mode 100644
index 88c77612..00000000
--- a/domain/consensus/processes/consensusstatemanager/finality.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package consensusstatemanager
-
-import (
-	"github.com/kaspanet/kaspad/domain/consensus/model"
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-)
-
-func (csm *consensusStateManager) checkFinalityViolation(
-	blockHash *externalapi.DomainHash) error {
-
-	log.Tracef("checkFinalityViolation start for block %s", blockHash)
-	defer log.Tracef("checkFinalityViolation end for block %s", blockHash)
-
-	if *blockHash == *csm.genesisHash {
-		log.Tracef("Block %s is the genesis block, "+
-			"and does not violate finality by definition", blockHash)
-		return nil
-	}
-
-	isViolatingFinality, err := csm.isViolatingFinality(blockHash)
-	if err != nil {
-		return err
-	}
-
-	if isViolatingFinality {
-		csm.blockStatusStore.Stage(blockHash, externalapi.StatusUTXOPendingVerification)
-		log.Warnf("Finality Violation Detected! Block %s violates finality!", blockHash)
-		return nil
-	}
-	log.Tracef("Block %s does not violate finality", blockHash)
-
-	return nil
-}
-
-func (csm *consensusStateManager) virtualFinalityPoint() (
-	*externalapi.DomainHash, error) {
-
-	log.Tracef("virtualFinalityPoint start")
-	defer log.Tracef("virtualFinalityPoint end")
-
-	virtualFinalityPoint, err := csm.dagTraversalManager.BlockAtDepth(
-		model.VirtualBlockHash, csm.finalityDepth)
-	if err != nil {
-		return nil, err
-	}
-	log.Tracef("The current virtual finality block is: %s", virtualFinalityPoint)
-
-	return virtualFinalityPoint, nil
-}
-
-func (csm *consensusStateManager) isViolatingFinality(
-	blockHash *externalapi.DomainHash) (bool, error) {
-
-	log.Tracef("isViolatingFinality start for block %s", blockHash)
-	defer log.Tracef("isViolatingFinality end for block %s", blockHash)
-
-	virtualFinalityPoint, err := csm.virtualFinalityPoint()
-	if err != nil {
-		return false, err
-	}
-	log.Tracef("The virtual finality point is: %s", virtualFinalityPoint)
-
-	isInSelectedParentChain, err := csm.dagTopologyManager.IsInSelectedParentChainOf(virtualFinalityPoint, blockHash)
-	if err != nil {
-		return false, err
-	}
-	log.Tracef("Is the virtual finality point %s "+
-		"in the selected parent chain of %s: %t", virtualFinalityPoint, blockHash, isInSelectedParentChain)
-
-	return !isInSelectedParentChain, nil
-}
diff --git a/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go b/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
index 84af3bf6..c85f0389 100644
--- a/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
+++ b/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
@@ -202,7 +202,7 @@ func (csm *consensusStateManager) boundedMergeBreakingParents(
 	}
 	log.Tracef("The potentially kosherizing blocks are: %s", potentiallyKosherizingBlocks)
 
-	virtualFinalityPoint, err := csm.virtualFinalityPoint()
+	virtualFinalityPoint, err := csm.finalityManager.VirtualFinalityPoint()
 	if err != nil {
 		return nil, err
 	}
diff --git a/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go b/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go
index f939a7ec..245fbfb1 100644
--- a/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go
+++ b/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go
@@ -3,6 +3,7 @@ package dagtopologymanager
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/pkg/errors"
 )
 
 // dagTopologyManager exposes methods for querying relationships
@@ -10,6 +11,7 @@ import (
 type dagTopologyManager struct {
 	reachabilityManager model.ReachabilityManager
 	blockRelationStore  model.BlockRelationStore
+	ghostdagStore       model.GHOSTDAGDataStore
 	databaseContext     model.DBReader
 }
 
@@ -17,12 +19,14 @@ type dagTopologyManager struct {
 func New(
 	databaseContext model.DBReader,
 	reachabilityManager model.ReachabilityManager,
-	blockRelationStore model.BlockRelationStore) model.DAGTopologyManager {
+	blockRelationStore model.BlockRelationStore,
+	ghostdagStore model.GHOSTDAGDataStore) model.DAGTopologyManager {
 
 	return &dagTopologyManager{
 		databaseContext:     databaseContext,
 		reachabilityManager: reachabilityManager,
 		blockRelationStore:  blockRelationStore,
+		ghostdagStore:       ghostdagStore,
 	}
 }
 
@@ -160,3 +164,36 @@ func (dtm *dagTopologyManager) SetParents(blockHash *externalapi.DomainHash, par
 
 	return nil
 }
+
+// ChildInSelectedParentChainOf returns the child of `context` that is in the selected-parent-chain of `highHash`
+func (dtm *dagTopologyManager) ChildInSelectedParentChainOf(
+	blockHash, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
+
+	// Virtual doesn't have reachability data, therefore, it should be treated as a special case -
+	// use it's selected parent as highHash.
+	specifiedHighHash := highHash
+	if highHash == model.VirtualBlockHash {
+		ghostdagData, err := dtm.ghostdagStore.Get(dtm.databaseContext, highHash)
+		if err != nil {
+			return nil, err
+		}
+		selectedParent := ghostdagData.SelectedParent()
+
+		// In case where `blockHash` is an immediate parent of `highHash`
+		if *blockHash == *selectedParent {
+			return highHash, nil
+		}
+		highHash = selectedParent
+	}
+
+	isInSelectedParentChain, err := dtm.IsInSelectedParentChainOf(blockHash, highHash)
+	if err != nil {
+		return nil, err
+	}
+	if !isInSelectedParentChain {
+		return nil, errors.Errorf("blockHash(%s) is not in the selected-parent-chain of highHash(%s)",
+			blockHash, specifiedHighHash)
+	}
+
+	return dtm.reachabilityManager.FindAncestorOfThisAmongChildrenOfOther(highHash, blockHash)
+}
diff --git a/domain/consensus/processes/finalitymanager/finality_manager.go b/domain/consensus/processes/finalitymanager/finality_manager.go
new file mode 100644
index 00000000..7fd8cf30
--- /dev/null
+++ b/domain/consensus/processes/finalitymanager/finality_manager.go
@@ -0,0 +1,144 @@
+package finalitymanager
+
+import (
+	"errors"
+
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/infrastructure/db/database"
+)
+
+type finalityManager struct {
+	databaseContext    model.DBReader
+	dagTopologyManager model.DAGTopologyManager
+	finalityStore      model.FinalityStore
+	ghostdagDataStore  model.GHOSTDAGDataStore
+	genesisHash        *externalapi.DomainHash
+	finalityDepth      uint64
+}
+
+// New instantiates a new FinalityManager
+func New(databaseContext model.DBReader,
+	dagTopologyManager model.DAGTopologyManager,
+	finalityStore model.FinalityStore,
+	ghostdagDataStore model.GHOSTDAGDataStore,
+	genesisHash *externalapi.DomainHash,
+	finalityDepth uint64) model.FinalityManager {
+
+	return &finalityManager{
+		databaseContext:    databaseContext,
+		genesisHash:        genesisHash,
+		dagTopologyManager: dagTopologyManager,
+		finalityStore:      finalityStore,
+		ghostdagDataStore:  ghostdagDataStore,
+		finalityDepth:      finalityDepth,
+	}
+}
+
+func (fm *finalityManager) IsViolatingFinality(blockHash *externalapi.DomainHash) (bool, error) {
+	if *blockHash == *fm.genesisHash {
+		log.Tracef("Block %s is the genesis block, "+
+			"and does not violate finality by definition", blockHash)
+		return false, nil
+	}
+	log.Tracef("isViolatingFinality start for block %s", blockHash)
+	defer log.Tracef("isViolatingFinality end for block %s", blockHash)
+
+	virtualFinalityPoint, err := fm.VirtualFinalityPoint()
+	if err != nil {
+		return false, err
+	}
+	log.Tracef("The virtual finality point is: %s", virtualFinalityPoint)
+
+	isInSelectedParentChain, err := fm.dagTopologyManager.IsInSelectedParentChainOf(virtualFinalityPoint, blockHash)
+	if err != nil {
+		return false, err
+	}
+	log.Tracef("Is the virtual finality point %s "+
+		"in the selected parent chain of %s: %t", virtualFinalityPoint, blockHash, isInSelectedParentChain)
+
+	return !isInSelectedParentChain, nil
+}
+
+func (fm *finalityManager) VirtualFinalityPoint() (*externalapi.DomainHash, error) {
+	log.Tracef("virtualFinalityPoint start")
+	defer log.Tracef("virtualFinalityPoint end")
+
+	virtualFinalityPoint, err := fm.calculateFinalityPoint(model.VirtualBlockHash)
+	if err != nil {
+		return nil, err
+	}
+	log.Tracef("The current virtual finality block is: %s", virtualFinalityPoint)
+
+	return virtualFinalityPoint, nil
+}
+
+func (fm *finalityManager) FinalityPoint(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
+	log.Tracef("FinalityPoint start")
+	defer log.Tracef("FinalityPoint end")
+	if *blockHash == *model.VirtualBlockHash {
+		return fm.VirtualFinalityPoint()
+	}
+	finalityPoint, err := fm.finalityStore.FinalityPoint(fm.databaseContext, blockHash)
+	if err != nil {
+		log.Tracef("%s finality point not found in store - calculating", blockHash)
+		if errors.Is(err, database.ErrNotFound) {
+			return fm.calculateAndStageFinalityPoint(blockHash)
+		}
+		return nil, err
+	}
+	return finalityPoint, nil
+}
+
+func (fm *finalityManager) calculateAndStageFinalityPoint(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
+	finalityPoint, err := fm.calculateFinalityPoint(blockHash)
+	if err != nil {
+		return nil, err
+	}
+	fm.finalityStore.StageFinalityPoint(blockHash, finalityPoint)
+	return finalityPoint, nil
+}
+
+func (fm *finalityManager) calculateFinalityPoint(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
+	log.Tracef("calculateFinalityPoint start")
+	defer log.Tracef("calculateFinalityPoint end")
+	ghostdagData, err := fm.ghostdagDataStore.Get(fm.databaseContext, blockHash)
+	if err != nil {
+		return nil, err
+	}
+
+	if ghostdagData.BlueScore() < fm.finalityDepth {
+		log.Tracef("%s blue score lower then finality depth - returning genesis as finality point", blockHash)
+		return fm.genesisHash, nil
+	}
+
+	selectedParent := ghostdagData.SelectedParent()
+	if *selectedParent == *fm.genesisHash {
+		return fm.genesisHash, nil
+	}
+
+	current, err := fm.finalityStore.FinalityPoint(fm.databaseContext, ghostdagData.SelectedParent())
+	if err != nil {
+		return nil, err
+	}
+	requiredBlueScore := ghostdagData.BlueScore() - fm.finalityDepth
+	log.Tracef("%s's finality point is the one having the highest blue score lower then %d", blockHash, requiredBlueScore)
+
+	var next *externalapi.DomainHash
+	for {
+		next, err = fm.dagTopologyManager.ChildInSelectedParentChainOf(current, blockHash)
+		if err != nil {
+			return nil, err
+		}
+		nextGHOSTDAGData, err := fm.ghostdagDataStore.Get(fm.databaseContext, next)
+		if err != nil {
+			return nil, err
+		}
+		if nextGHOSTDAGData.BlueScore() >= requiredBlueScore {
+			log.Tracef("%s's finality point is %s", blockHash, current)
+			return current, nil
+		}
+
+		current = next
+	}
+}
diff --git a/domain/consensus/processes/finalitymanager/log.go b/domain/consensus/processes/finalitymanager/log.go
new file mode 100644
index 00000000..05595b91
--- /dev/null
+++ b/domain/consensus/processes/finalitymanager/log.go
@@ -0,0 +1,7 @@
+package finalitymanager
+
+import (
+	"github.com/kaspanet/kaspad/infrastructure/logger"
+)
+
+var log, _ = logger.Get(logger.SubsystemTags.BDAG)
diff --git a/domain/consensus/processes/mergedepthmanager/merge_depth_manager.go b/domain/consensus/processes/mergedepthmanager/merge_depth_manager.go
index 155dcd73..66bb72a4 100644
--- a/domain/consensus/processes/mergedepthmanager/merge_depth_manager.go
+++ b/domain/consensus/processes/mergedepthmanager/merge_depth_manager.go
@@ -8,27 +8,27 @@ import (
 )
 
 type mergeDepthManager struct {
-	finalityDepth uint64
-
 	databaseContext     model.DBReader
 	dagTopologyManager  model.DAGTopologyManager
 	dagTraversalManager model.DAGTraversalManager
+	finalityManager     model.FinalityManager
 
 	ghostdagDataStore model.GHOSTDAGDataStore
 }
 
 // New instantiates a new MergeDepthManager
-func New(finalityDepth uint64,
+func New(
 	databaseContext model.DBReader,
 	dagTopologyManager model.DAGTopologyManager,
 	dagTraversalManager model.DAGTraversalManager,
+	finalityManager model.FinalityManager,
 	ghostdagDataStore model.GHOSTDAGDataStore) model.MergeDepthManager {
 
 	return &mergeDepthManager{
-		finalityDepth:       finalityDepth,
 		databaseContext:     databaseContext,
 		dagTopologyManager:  dagTopologyManager,
 		dagTraversalManager: dagTraversalManager,
+		finalityManager:     finalityManager,
 		ghostdagDataStore:   ghostdagDataStore,
 	}
 
@@ -50,7 +50,7 @@ func (mdm *mergeDepthManager) CheckBoundedMergeDepth(blockHash *externalapi.Doma
 		return nil
 	}
 
-	finalityPoint, err := mdm.finalityPoint(blockHash)
+	finalityPoint, err := mdm.finalityManager.FinalityPoint(blockHash)
 	if err != nil {
 		return err
 	}
@@ -102,14 +102,10 @@ func (mdm mergeDepthManager) NonBoundedMergeDepthViolatingBlues(blockHash *exter
 }
 
 func (mdm *mergeDepthManager) hasFinalityPointInOthersSelectedChain(this, other *externalapi.DomainHash) (bool, error) {
-	finalityPoint, err := mdm.finalityPoint(this)
+	finalityPoint, err := mdm.finalityManager.FinalityPoint(this)
 	if err != nil {
 		return false, err
 	}
 
 	return mdm.dagTopologyManager.IsInSelectedParentChainOf(finalityPoint, other)
 }
-
-func (mdm *mergeDepthManager) finalityPoint(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
-	return mdm.dagTraversalManager.BlockAtDepth(blockHash, mdm.finalityDepth)
-}
diff --git a/domain/consensus/processes/reachabilitymanager/tree.go b/domain/consensus/processes/reachabilitymanager/tree.go
index 1dea962e..054b3836 100644
--- a/domain/consensus/processes/reachabilitymanager/tree.go
+++ b/domain/consensus/processes/reachabilitymanager/tree.go
@@ -163,7 +163,7 @@ func (rt *reachabilityManager) addChild(node, child, reindexRoot *externalapi.Do
 	// Temporarily set the child's interval to be empty, at
 	// the start of node's remaining interval. This is done
 	// so that child-of-node checks (e.g.
-	// findAncestorOfThisAmongChildrenOfOther) will not fail for node.
+	// FindAncestorOfThisAmongChildrenOfOther) will not fail for node.
 	err = rt.stageInterval(child, newReachabilityInterval(remaining.Start, remaining.Start-1))
 	if err != nil {
 		return err
@@ -411,7 +411,7 @@ func (rt *reachabilityManager) reindexIntervalsEarlierThanReindexRoot(node,
 	// The chosen child is:
 	// a. A reachability tree child of `commonAncestor`
 	// b. A reachability tree ancestor of `reindexRoot`
-	commonAncestorChosenChild, err := rt.findAncestorOfThisAmongChildrenOfOther(reindexRoot, commonAncestor)
+	commonAncestorChosenChild, err := rt.FindAncestorOfThisAmongChildrenOfOther(reindexRoot, commonAncestor)
 	if err != nil {
 		return err
 	}
@@ -465,7 +465,7 @@ func (rt *reachabilityManager) reclaimIntervalBeforeChosenChild(rtn, commonAnces
 				break
 			}
 
-			current, err = rt.findAncestorOfThisAmongChildrenOfOther(reindexRoot, current)
+			current, err = rt.FindAncestorOfThisAmongChildrenOfOther(reindexRoot, current)
 			if err != nil {
 				return err
 			}
@@ -587,7 +587,7 @@ func (rt *reachabilityManager) reclaimIntervalAfterChosenChild(node, commonAnces
 				break
 			}
 
-			current, err = rt.findAncestorOfThisAmongChildrenOfOther(reindexRoot, current)
+			current, err = rt.FindAncestorOfThisAmongChildrenOfOther(reindexRoot, current)
 			if err != nil {
 				return err
 			}
@@ -799,7 +799,7 @@ func (rt *reachabilityManager) maybeMoveReindexRoot(reindexRoot, newTreeNode *ex
 		return commonAncestor, true, nil
 	}
 
-	reindexRootChosenChild, err := rt.findAncestorOfThisAmongChildrenOfOther(newTreeNode, reindexRoot)
+	reindexRootChosenChild, err := rt.FindAncestorOfThisAmongChildrenOfOther(newTreeNode, reindexRoot)
 	if err != nil {
 		return nil, false, err
 	}
@@ -826,9 +826,9 @@ func (rt *reachabilityManager) maybeMoveReindexRoot(reindexRoot, newTreeNode *ex
 	return reindexRootChosenChild, true, nil
 }
 
-// findAncestorOfThisAmongChildrenOfOther finds the reachability tree child
+// FindAncestorOfThisAmongChildrenOfOther finds the reachability tree child
 // of node that is the ancestor of node.
-func (rt *reachabilityManager) findAncestorOfThisAmongChildrenOfOther(this, other *externalapi.DomainHash) (*externalapi.DomainHash, error) {
+func (rt *reachabilityManager) FindAncestorOfThisAmongChildrenOfOther(this, other *externalapi.DomainHash) (*externalapi.DomainHash, error) {
 	otherChildren, err := rt.children(other)
 	if err != nil {
 		return nil, err


Test patch:
diff --git a/domain/consensus/finality_test.go b/domain/consensus/finality_test.go
index 856a02dc..8eb2c873 100644
--- a/domain/consensus/finality_test.go
+++ b/domain/consensus/finality_test.go
@@ -47,13 +47,13 @@ func TestFinality(t *testing.T) {
 		for i := uint64(0); i < finalityInterval-1; i++ {
 			mainChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{mainChainTipHash})
 			if err != nil {
-				t.Fatalf("TestFinality: Failed to process Block #%d: %v", i, err)
+				t.Fatalf("TestFinality: Failed to process Block #%d: %+v", i, err)
 			}
 			mainChainTipHash = consensushashing.BlockHash(mainChainTip)
 
 			blockInfo, err := consensus.GetBlockInfo(mainChainTipHash)
 			if err != nil {
-				t.Fatalf("TestFinality: Block #%d failed to get info: %v", i, err)
+				t.Fatalf("TestFinality: Block #%d failed to get info: %+v", i, err)
 			}
 			if blockInfo.BlockStatus != externalapi.StatusValid {
 				t.Fatalf("Block #%d in main chain expected to have status '%s', but got '%s'",
@@ -120,7 +120,7 @@ func TestFinality(t *testing.T) {
 			mainChainTipHash = consensushashing.BlockHash(mainChainTip)
 		}
 
-		virtualFinality, err := consensus.ConsensusStateManager().VirtualFinalityPoint()
+		virtualFinality, err := consensus.FinalityManager().VirtualFinalityPoint()
 		if err != nil {
 			t.Fatalf("TestFinality: Failed getting the virtual's finality point: %v", err)
 		}
diff --git a/domain/consensus/model/testapi/test_consensus.go b/domain/consensus/model/testapi/test_consensus.go
index 9bf365e9..9800400a 100644
--- a/domain/consensus/model/testapi/test_consensus.go
+++ b/domain/consensus/model/testapi/test_consensus.go
@@ -41,6 +41,7 @@ type TestConsensus interface {
 	BlockValidator() model.BlockValidator
 	CoinbaseManager() model.CoinbaseManager
 	ConsensusStateManager() TestConsensusStateManager
+	FinalityManager() model.FinalityManager
 	DAGTopologyManager() model.DAGTopologyManager
 	DAGTraversalManager() model.DAGTraversalManager
 	DifficultyManager() model.DifficultyManager
diff --git a/domain/consensus/model/testapi/test_consensus_state_manager.go b/domain/consensus/model/testapi/test_consensus_state_manager.go
index 251eae8a..b2f30aa1 100644
--- a/domain/consensus/model/testapi/test_consensus_state_manager.go
+++ b/domain/consensus/model/testapi/test_consensus_state_manager.go
@@ -11,5 +11,4 @@ type TestConsensusStateManager interface {
 	AddUTXOToMultiset(multiset model.Multiset, entry externalapi.UTXOEntry,
 		outpoint *externalapi.DomainOutpoint) error
 	ResolveBlockStatus(blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error)
-	VirtualFinalityPoint() (*externalapi.DomainHash, error)
 }
diff --git a/domain/consensus/processes/consensusstatemanager/test_consensus_state_manager.go b/domain/consensus/processes/consensusstatemanager/test_consensus_state_manager.go
index e10edc82..8efc08d8 100644
--- a/domain/consensus/processes/consensusstatemanager/test_consensus_state_manager.go
+++ b/domain/consensus/processes/consensusstatemanager/test_consensus_state_manager.go
@@ -24,7 +24,3 @@ func (csm testConsensusStateManager) AddUTXOToMultiset(
 func (csm testConsensusStateManager) ResolveBlockStatus(blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error) {
 	return csm.resolveBlockStatus(blockHash)
 }
-
-func (csm testConsensusStateManager) VirtualFinalityPoint() (*externalapi.DomainHash, error) {
-	return csm.virtualFinalityPoint()
-}
diff --git a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
index 49208549..f41c9cf1 100644
--- a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
+++ b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
@@ -2,14 +2,15 @@ package ghostdagmanager_test
 
 import (
 	"encoding/json"
-	"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
-	"github.com/kaspanet/kaspad/util"
 	"math/big"
 	"os"
 	"path/filepath"
 	"reflect"
 	"testing"
 
+	"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
+	"github.com/kaspanet/kaspad/util"
+
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2"
@@ -226,6 +227,10 @@ type DAGTopologyManagerImpl struct {
 	parentsMap map[externalapi.DomainHash][]*externalapi.DomainHash
 }
 
+func (dt *DAGTopologyManagerImpl) ChildInSelectedParentChainOf(context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
+	panic("implement me")
+}
+
 func (dt *DAGTopologyManagerImpl) Tips() ([]*externalapi.DomainHash, error) {
 	panic("implement me")
 }
diff --git a/domain/consensus/test_consensus_getters.go b/domain/consensus/test_consensus_getters.go
index 8a668cc9..539bdcb0 100644
--- a/domain/consensus/test_consensus_getters.go
+++ b/domain/consensus/test_consensus_getters.go
@@ -120,3 +120,11 @@ func (tc *testConsensus) SyncManager() model.SyncManager {
 func (tc *testConsensus) TransactionValidator() testapi.TestTransactionValidator {
 	return tc.testTransactionValidator
 }
+
+func (tc *testConsensus) FinalityManager() model.FinalityManager {
+	return tc.finalityManager
+}
+
+func (tc *testConsensus) FinalityStore() model.FinalityStore {
+	return tc.finalityStore
+}