mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[NOD-1590] Implement optimized finalityPoint calculation mechanism (#1190)
* [NOD-1590] Moved all finality logic to FinalityManager * [NOD-1590] Add finality store * [NOD-1590] Implement optimized finalityPoint calculation mechanism * [NOD-1590] Add comments * [NOD-1590] Add finalityStore to consensus object, and TestConsensus * [NOD-1590] Added logs to finalityPoint calculation
This commit is contained in:
parent
37bf261da1
commit
b7ca3f4461
@ -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
|
||||
|
@ -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[:])
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
144
domain/consensus/processes/finalitymanager/finality_manager.go
Normal file
144
domain/consensus/processes/finalitymanager/finality_manager.go
Normal file
@ -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
|
||||
}
|
||||
}
|
7
domain/consensus/processes/finalitymanager/log.go
Normal file
7
domain/consensus/processes/finalitymanager/log.go
Normal file
@ -0,0 +1,7 @@
|
||||
package finalitymanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.BDAG)
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user