mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-06 22:26:47 +00:00
New headers first flow (#1211)
* Get rid of insertMode * Rename AddBlockToVirtual->AddBlock * When F is not in the future of P, enforce finality with P and not with F. * Don't allow blocks with invalid parents or with missing block body * Check finality violation before checking block status * Implement CalculateIndependentPruningPoint * Move checkBlockStatus to validateBlock * Add ValidateBlock to block processor interface * Adjust SetPruningPoint to the new IBD flow * Add pruning store to CSM's constructor * Flip wrong condition on AddHeaderTip * Fix func (hts *headerSelectedTipStore) Has * Fix block stage order * Call to ValidateBodyInContext from validatePostProofOfWork * Enable overrideDAGParams * Update log * Rename SetPruningPoint to ValidateAndInsertPruningPoint and move most of its logic inside block processor * Rename hasValidatedHeader->hasValidatedOnlyHeader * Fix typo * Name return values for fetchMissingUTXOSet * Add comment * Return ErrMissingParents when block body is missing * Add logs and comments * Fix merge error * Fix pruning point calculation to be by virtual selected parent * Replace CalculateIndependentPruningPoint to CalculatePruningPointByHeaderSelectedTip * Fix isAwaitingUTXOSet to check pruning point by headers * Change isAwaitingUTXOSet indication * Remove IsBlockInHeaderPruningPointFuture from BlockInfo * Fix LowestChainBlockAboveOrEqualToBlueScore * Add validateNewPruningPointTransactions * Add validateNewPruningAgainstPastUTXO * Rename set_pruning_utxo_set.go to update_pruning_utxo_set.go * Check missing block body hashes by missing block instead of status * Validate pruning point against past UTXO with the pruning point as block hash * Remove virtualHeaderHash * Fix comment * Fix imports
This commit is contained in:
parent
6926a7ab81
commit
48e1a2c396
@ -212,7 +212,6 @@ func (flow *handleRelayInvsFlow) processBlock(block *externalapi.DomainBlock) ([
|
||||
return missingParentsError.MissingParentHashes, nil
|
||||
}
|
||||
log.Warnf("Rejected block %s from %s: %s", blockHash, flow.peer, err)
|
||||
|
||||
return nil, protocolerrors.Wrapf(true, err, "got invalid block %s from relay", blockHash)
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -35,7 +35,7 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if syncInfo.State == externalapi.SyncStateAwaitingUTXOSet {
|
||||
if syncInfo.IsAwaitingUTXOSet {
|
||||
found, err := flow.fetchMissingUTXOSet(syncInfo.IBDRootUTXOBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -190,8 +190,8 @@ func (flow *handleRelayInvsFlow) processHeader(msgBlockHeader *appmessage.MsgBlo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(ibdRootHash *externalapi.DomainHash) (bool, error) {
|
||||
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDRootUTXOSetAndBlock(ibdRootHash))
|
||||
func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(ibdRootHash *externalapi.DomainHash) (succeed bool, err error) {
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDRootUTXOSetAndBlock(ibdRootHash))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -205,17 +205,23 @@ func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(ibdRootHash *externalapi.Do
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "got invalid block %s during IBD", blockHash)
|
||||
}
|
||||
|
||||
err = flow.Domain().Consensus().SetPruningPointUTXOSet(utxoSet)
|
||||
err = flow.Domain().Consensus().ValidateAndInsertPruningPoint(block, utxoSet)
|
||||
if err != nil {
|
||||
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "error with IBD root UTXO set")
|
||||
}
|
||||
|
||||
syncInfo, err := flow.Domain().Consensus().GetSyncInfo()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// TODO: Find a better way to deal with finality conflicts.
|
||||
if syncInfo.IsAwaitingUTXOSet {
|
||||
log.Warnf("Still awaiting for UTXO set. This can happen only because the given pruning point violates " +
|
||||
"finality. If this keeps happening delete the data directory and restart your node.")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
@ -25,25 +25,25 @@ type consensus struct {
|
||||
dagTraversalManager model.DAGTraversalManager
|
||||
difficultyManager model.DifficultyManager
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
headerTipsManager model.HeaderTipsManager
|
||||
headerTipsManager model.HeadersSelectedTipManager
|
||||
mergeDepthManager model.MergeDepthManager
|
||||
pruningManager model.PruningManager
|
||||
reachabilityManager model.ReachabilityManager
|
||||
finalityManager model.FinalityManager
|
||||
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockStore model.BlockStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
pruningStore model.PruningStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
headerTipsStore model.HeaderTipsStore
|
||||
multisetStore model.MultisetStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
finalityStore model.FinalityStore
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockStore model.BlockStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
pruningStore model.PruningStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
headersSelectedTipStore model.HeaderSelectedTipStore
|
||||
multisetStore model.MultisetStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
finalityStore model.FinalityStore
|
||||
}
|
||||
|
||||
// BuildBlock builds a block over the current state, with the transactions
|
||||
@ -138,12 +138,6 @@ func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalap
|
||||
|
||||
blockInfo.BlueScore = ghostdagData.BlueScore()
|
||||
|
||||
isBlockInHeaderPruningPointFuture, err := s.syncManager.IsBlockInHeaderPruningPointFuture(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockInfo.IsBlockInHeaderPruningPointFuture = isBlockInHeaderPruningPointFuture
|
||||
|
||||
return blockInfo, nil
|
||||
}
|
||||
|
||||
@ -183,11 +177,11 @@ func (s *consensus) GetPruningPointUTXOSet(expectedPruningPointHash *externalapi
|
||||
return serializedUTXOSet, nil
|
||||
}
|
||||
|
||||
func (s *consensus) SetPruningPointUTXOSet(serializedUTXOSet []byte) error {
|
||||
func (s *consensus) ValidateAndInsertPruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.consensusStateManager.SetPruningPointUTXOSet(serializedUTXOSet)
|
||||
return s.blockProcessor.ValidateAndInsertPruningPoint(newPruningPoint, serializedUTXOSet)
|
||||
}
|
||||
|
||||
func (s *consensus) GetVirtualSelectedParent() (*externalapi.DomainBlock, error) {
|
||||
@ -201,27 +195,6 @@ func (s *consensus) GetVirtualSelectedParent() (*externalapi.DomainBlock, error)
|
||||
return s.blockStore.Block(s.databaseContext, virtualGHOSTDAGData.SelectedParent())
|
||||
}
|
||||
|
||||
func (s *consensus) CreateBlockLocator(lowHash, highHash *externalapi.DomainHash, limit uint32) (externalapi.BlockLocator, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.CreateBlockLocator(lowHash, highHash, limit)
|
||||
}
|
||||
|
||||
func (s *consensus) FindNextBlockLocatorBoundaries(blockLocator externalapi.BlockLocator) (lowHash, highHash *externalapi.DomainHash, err error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.FindNextBlockLocatorBoundaries(blockLocator)
|
||||
}
|
||||
|
||||
func (s *consensus) GetSyncInfo() (*externalapi.SyncInfo, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.GetSyncInfo()
|
||||
}
|
||||
|
||||
func (s *consensus) Tips() ([]*externalapi.DomainHash, error) {
|
||||
return s.consensusStateStore.Tips(s.databaseContext)
|
||||
}
|
||||
@ -246,3 +219,24 @@ func (s *consensus) GetVirtualInfo() (*externalapi.VirtualInfo, error) {
|
||||
PastMedianTime: pastMedianTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *consensus) CreateBlockLocator(lowHash, highHash *externalapi.DomainHash, limit uint32) (externalapi.BlockLocator, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.CreateBlockLocator(lowHash, highHash, limit)
|
||||
}
|
||||
|
||||
func (s *consensus) FindNextBlockLocatorBoundaries(blockLocator externalapi.BlockLocator) (lowHash, highHash *externalapi.DomainHash, err error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.FindNextBlockLocatorBoundaries(blockLocator)
|
||||
}
|
||||
|
||||
func (s *consensus) GetSyncInfo() (*externalapi.SyncInfo, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.GetSyncInfo()
|
||||
}
|
||||
|
@ -42,9 +42,6 @@ func TestConsensus_GetBlockInfo(t *testing.T) {
|
||||
if info.BlockStatus != externalapi.StatusInvalid {
|
||||
t.Fatalf("Expected block status: %s, instead got: %s", externalapi.StatusInvalid, info.BlockStatus)
|
||||
}
|
||||
if info.IsBlockInHeaderPruningPointFuture != false {
|
||||
t.Fatalf("Expected IsBlockInHeaderPruningPointFuture=false, instead found: %t", info.IsBlockInHeaderPruningPointFuture)
|
||||
}
|
||||
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{}
|
||||
validBlock, err := consensus.BuildBlock(&emptyCoinbase, nil)
|
||||
@ -68,9 +65,6 @@ func TestConsensus_GetBlockInfo(t *testing.T) {
|
||||
if info.BlockStatus != externalapi.StatusValid {
|
||||
t.Fatalf("Expected block status: %s, instead got: %s", externalapi.StatusValid, info.BlockStatus)
|
||||
}
|
||||
if info.IsBlockInHeaderPruningPointFuture != true {
|
||||
t.Fatalf("Expected IsBlockInHeaderPruningPointFuture=true, instead found: %t", info.IsBlockInHeaderPruningPointFuture)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
@ -0,0 +1,100 @@
|
||||
package headersselectedtipstore
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
)
|
||||
|
||||
var headerSelectedTipKey = dbkeys.MakeBucket().Key([]byte("headers-selected-tip"))
|
||||
|
||||
type headerSelectedTipStore struct {
|
||||
staging *externalapi.DomainHash
|
||||
cache *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// New instantiates a new HeaderSelectedTipStore
|
||||
func New() model.HeaderSelectedTipStore {
|
||||
return &headerSelectedTipStore{}
|
||||
}
|
||||
|
||||
func (hts *headerSelectedTipStore) Has(dbContext model.DBReader) (bool, error) {
|
||||
if hts.staging != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if hts.cache != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(headerSelectedTipKey)
|
||||
}
|
||||
|
||||
func (hts *headerSelectedTipStore) Discard() {
|
||||
hts.staging = nil
|
||||
}
|
||||
|
||||
func (hts *headerSelectedTipStore) Commit(dbTx model.DBTransaction) error {
|
||||
if hts.staging == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
selectedTipBytes, err := hts.serializeHeadersSelectedTip(hts.staging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(headerSelectedTipKey, selectedTipBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hts.cache = hts.staging
|
||||
|
||||
hts.Discard()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hts *headerSelectedTipStore) Stage(selectedTip *externalapi.DomainHash) {
|
||||
hts.staging = selectedTip.Clone()
|
||||
}
|
||||
|
||||
func (hts *headerSelectedTipStore) IsStaged() bool {
|
||||
return hts.staging != nil
|
||||
}
|
||||
|
||||
func (hts *headerSelectedTipStore) HeadersSelectedTip(dbContext model.DBReader) (*externalapi.DomainHash, error) {
|
||||
if hts.staging != nil {
|
||||
return hts.staging.Clone(), nil
|
||||
}
|
||||
|
||||
if hts.cache != nil {
|
||||
return hts.cache.Clone(), nil
|
||||
}
|
||||
|
||||
selectedTipBytes, err := dbContext.Get(headerSelectedTipKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectedTip, err := hts.deserializeHeadersSelectedTip(selectedTipBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hts.cache = selectedTip
|
||||
return hts.cache.Clone(), nil
|
||||
}
|
||||
|
||||
func (hts *headerSelectedTipStore) serializeHeadersSelectedTip(selectedTip *externalapi.DomainHash) ([]byte, error) {
|
||||
return proto.Marshal(serialization.DomainHashToDbHash(selectedTip))
|
||||
}
|
||||
|
||||
func (hts *headerSelectedTipStore) deserializeHeadersSelectedTip(selectedTipBytes []byte) (*externalapi.DomainHash, error) {
|
||||
dbHash := &serialization.DbHash{}
|
||||
err := proto.Unmarshal(selectedTipBytes, dbHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serialization.DbHashToDomainHash(dbHash)
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
package headertipsstore
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
)
|
||||
|
||||
var headerTipsKey = dbkeys.MakeBucket().Key([]byte("header-tips"))
|
||||
|
||||
type headerTipsStore struct {
|
||||
staging []*externalapi.DomainHash
|
||||
cache []*externalapi.DomainHash
|
||||
}
|
||||
|
||||
// New instantiates a new HeaderTipsStore
|
||||
func New() model.HeaderTipsStore {
|
||||
return &headerTipsStore{}
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) HasTips(dbContext model.DBReader) (bool, error) {
|
||||
if len(hts.staging) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if len(hts.cache) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(headerTipsKey)
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) Discard() {
|
||||
hts.staging = nil
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) Commit(dbTx model.DBTransaction) error {
|
||||
if hts.staging == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tipsBytes, err := hts.serializeTips(hts.staging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(headerTipsKey, tipsBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hts.cache = hts.staging
|
||||
|
||||
hts.Discard()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) Stage(tips []*externalapi.DomainHash) {
|
||||
hts.staging = externalapi.CloneHashes(tips)
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) IsStaged() bool {
|
||||
return hts.staging != nil
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
|
||||
if hts.staging != nil {
|
||||
return externalapi.CloneHashes(hts.staging), nil
|
||||
}
|
||||
|
||||
if hts.cache != nil {
|
||||
return externalapi.CloneHashes(hts.cache), nil
|
||||
}
|
||||
|
||||
tipsBytes, err := dbContext.Get(headerTipsKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tips, err := hts.deserializeTips(tipsBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hts.cache = tips
|
||||
return externalapi.CloneHashes(tips), nil
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) serializeTips(tips []*externalapi.DomainHash) ([]byte, error) {
|
||||
dbTips := serialization.HeaderTipsToDBHeaderTips(tips)
|
||||
return proto.Marshal(dbTips)
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) deserializeTips(tipsBytes []byte) ([]*externalapi.DomainHash, error) {
|
||||
dbTips := &serialization.DbHeaderTips{}
|
||||
err := proto.Unmarshal(tipsBytes, dbTips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serialization.DBHeaderTipsToHeaderTips(dbTips)
|
||||
}
|
@ -17,7 +17,7 @@ import (
|
||||
"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/headersselectedtipstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/multisetstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/pruningstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/reachabilitydatastore"
|
||||
@ -32,7 +32,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtopologymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/difficultymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/headertipsmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/headersselectedtipmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/mergedepthmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/pastmediantimemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/pruningmanager"
|
||||
@ -81,7 +81,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
utxoDiffStore := utxodiffstore.New(200)
|
||||
consensusStateStore := consensusstatestore.New()
|
||||
ghostdagDataStore := ghostdagdatastore.New(10_000)
|
||||
headerTipsStore := headertipsstore.New()
|
||||
headersSelectedTipStore := headersselectedtipstore.New()
|
||||
finalityStore := finalitystore.New(200)
|
||||
|
||||
// Processes
|
||||
@ -139,7 +139,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
dagParams.CoinbasePayloadScriptPublicKeyMaxLength,
|
||||
ghostdagDataStore,
|
||||
acceptanceDataStore)
|
||||
headerTipsManager := headertipsmanager.New(dbManager, dagTopologyManager, ghostdagManager, headerTipsStore)
|
||||
headerTipsManager := headersselectedtipmanager.New(dbManager, dagTopologyManager, ghostdagManager, headersSelectedTipStore)
|
||||
genesisHash := dagParams.GenesisHash
|
||||
finalityManager := finalitymanager.New(
|
||||
dbManager,
|
||||
@ -211,7 +211,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
blockRelationStore,
|
||||
acceptanceDataStore,
|
||||
blockHeaderStore,
|
||||
headerTipsStore)
|
||||
headersSelectedTipStore,
|
||||
pruningStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -225,6 +226,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
ghostdagDataStore,
|
||||
pruningStore,
|
||||
blockStatusStore,
|
||||
headersSelectedTipStore,
|
||||
multisetStore,
|
||||
acceptanceDataStore,
|
||||
blockStore,
|
||||
@ -236,17 +238,16 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
syncManager := syncmanager.New(
|
||||
dbManager,
|
||||
genesisHash,
|
||||
dagParams.TargetTimePerBlock.Milliseconds(),
|
||||
dagTraversalManager,
|
||||
dagTopologyManager,
|
||||
ghostdagManager,
|
||||
consensusStateManager,
|
||||
pruningManager,
|
||||
|
||||
ghostdagDataStore,
|
||||
blockStatusStore,
|
||||
blockHeaderStore,
|
||||
headerTipsStore,
|
||||
blockStore)
|
||||
blockStore,
|
||||
pruningStore)
|
||||
|
||||
blockBuilder := blockbuilder.New(
|
||||
dbManager,
|
||||
@ -287,7 +288,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
reachabilityDataStore,
|
||||
utxoDiffStore,
|
||||
blockHeaderStore,
|
||||
headerTipsStore,
|
||||
headersSelectedTipStore,
|
||||
finalityStore)
|
||||
|
||||
c := &consensus{
|
||||
@ -312,19 +313,19 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
reachabilityManager: reachabilityManager,
|
||||
finalityManager: finalityManager,
|
||||
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockStore: blockStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
pruningStore: pruningStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
consensusStateStore: consensusStateStore,
|
||||
headerTipsStore: headerTipsStore,
|
||||
multisetStore: multisetStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
finalityStore: finalityStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockStore: blockStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
pruningStore: pruningStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
consensusStateStore: consensusStateStore,
|
||||
headersSelectedTipStore: headersSelectedTipStore,
|
||||
multisetStore: multisetStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
finalityStore: finalityStore,
|
||||
}
|
||||
|
||||
genesisInfo, err := c.GetBlockInfo(genesisHash)
|
||||
|
@ -5,6 +5,4 @@ type BlockInfo struct {
|
||||
Exists bool
|
||||
BlockStatus BlockStatus
|
||||
BlueScore uint64
|
||||
|
||||
IsBlockInHeaderPruningPointFuture bool
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ type Consensus interface {
|
||||
GetHashesBetween(lowHash, highHash *DomainHash) ([]*DomainHash, error)
|
||||
GetMissingBlockBodyHashes(highHash *DomainHash) ([]*DomainHash, error)
|
||||
GetPruningPointUTXOSet(expectedPruningPointHash *DomainHash) ([]byte, error)
|
||||
SetPruningPointUTXOSet(serializedUTXOSet []byte) error
|
||||
ValidateAndInsertPruningPoint(newPruningPoint *DomainBlock, serializedUTXOSet []byte) error
|
||||
GetVirtualSelectedParent() (*DomainBlock, error)
|
||||
CreateBlockLocator(lowHash, highHash *DomainHash, limit uint32) (BlockLocator, error)
|
||||
FindNextBlockLocatorBoundaries(blockLocator BlockLocator) (lowHash, highHash *DomainHash, err error)
|
||||
|
@ -1,37 +1,8 @@
|
||||
package externalapi
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Each of the following represent one of the possible sync
|
||||
// states of the consensus
|
||||
const (
|
||||
SyncStateSynced SyncState = iota
|
||||
SyncStateAwaitingGenesis
|
||||
SyncStateAwaitingUTXOSet
|
||||
SyncStateAwaitingBlockBodies
|
||||
)
|
||||
|
||||
// SyncState represents the current sync state of the consensus
|
||||
type SyncState uint8
|
||||
|
||||
func (s SyncState) String() string {
|
||||
switch s {
|
||||
case SyncStateSynced:
|
||||
return "SyncStateSynced"
|
||||
case SyncStateAwaitingGenesis:
|
||||
return "SyncStateAwaitingGenesis"
|
||||
case SyncStateAwaitingUTXOSet:
|
||||
return "SyncStateAwaitingUTXOSet"
|
||||
case SyncStateAwaitingBlockBodies:
|
||||
return "SyncStateAwaitingBlockBodies"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<unknown state (%d)>", s)
|
||||
}
|
||||
|
||||
// SyncInfo holds info about the current sync state of the consensus
|
||||
type SyncInfo struct {
|
||||
State SyncState
|
||||
IsAwaitingUTXOSet bool
|
||||
IBDRootUTXOBlockHash *DomainHash
|
||||
HeaderCount uint64
|
||||
BlockCount uint64
|
||||
|
@ -2,11 +2,11 @@ package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// HeaderTipsStore represents a store of the header tips
|
||||
type HeaderTipsStore interface {
|
||||
// HeaderSelectedTipStore represents a store of the headers selected tip
|
||||
type HeaderSelectedTipStore interface {
|
||||
Store
|
||||
Stage(tips []*externalapi.DomainHash)
|
||||
Stage(selectedTip *externalapi.DomainHash)
|
||||
IsStaged() bool
|
||||
Tips(dbContext DBReader) ([]*externalapi.DomainHash, error)
|
||||
HasTips(dbContext DBReader) (bool, error)
|
||||
HeadersSelectedTip(dbContext DBReader) (*externalapi.DomainHash, error)
|
||||
Has(dbContext DBReader) (bool, error)
|
||||
}
|
||||
|
@ -5,4 +5,5 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
// BlockProcessor is responsible for processing incoming blocks
|
||||
type BlockProcessor interface {
|
||||
ValidateAndInsertBlock(block *externalapi.DomainBlock) error
|
||||
ValidateAndInsertPruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// ConsensusStateManager manages the node's consensus state
|
||||
type ConsensusStateManager interface {
|
||||
AddBlockToVirtual(blockHash *externalapi.DomainHash) error
|
||||
AddBlock(blockHash *externalapi.DomainHash) error
|
||||
PopulateTransactionWithUTXOEntries(transaction *externalapi.DomainTransaction) error
|
||||
SetPruningPointUTXOSet(serializedUTXOSet []byte) error
|
||||
UpdatePruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error
|
||||
RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (ReadOnlyUTXOSetIterator, error)
|
||||
HeaderTipsPruningPoint() (*externalapi.DomainHash, error)
|
||||
CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (UTXODiff, AcceptanceData, Multiset, error)
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ 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)
|
||||
}
|
||||
|
@ -2,8 +2,7 @@ package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// HeaderTipsManager manages the state of the header tips
|
||||
type HeaderTipsManager interface {
|
||||
// HeadersSelectedTipManager manages the state of the headers selected tip
|
||||
type HeadersSelectedTipManager interface {
|
||||
AddHeaderTip(hash *externalapi.DomainHash) error
|
||||
SelectedTip() (*externalapi.DomainHash, error)
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// PruningManager resolves and manages the current pruning point
|
||||
type PruningManager interface {
|
||||
FindNextPruningPoint() error
|
||||
UpdatePruningPointByVirtual() error
|
||||
CalculatePruningPointByHeaderSelectedTip() (*externalapi.DomainHash, error)
|
||||
}
|
||||
|
@ -8,6 +8,5 @@ type SyncManager interface {
|
||||
GetMissingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
CreateBlockLocator(lowHash, highHash *externalapi.DomainHash, limit uint32) (externalapi.BlockLocator, error)
|
||||
FindNextBlockLocatorBoundaries(blockLocator externalapi.BlockLocator) (lowHash, highHash *externalapi.DomainHash, err error)
|
||||
IsBlockInHeaderPruningPointFuture(blockHash *externalapi.DomainHash) (bool, error)
|
||||
GetSyncInfo() (*externalapi.SyncInfo, error)
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ type TestConsensus interface {
|
||||
BlockStore() model.BlockStore
|
||||
ConsensusStateStore() model.ConsensusStateStore
|
||||
GHOSTDAGDataStore() model.GHOSTDAGDataStore
|
||||
HeaderTipsStore() model.HeaderTipsStore
|
||||
HeaderTipsStore() model.HeaderSelectedTipStore
|
||||
MultisetStore() model.MultisetStore
|
||||
PruningStore() model.PruningStore
|
||||
ReachabilityDataStore() model.ReachabilityDataStore
|
||||
@ -46,7 +46,7 @@ type TestConsensus interface {
|
||||
DAGTraversalManager() model.DAGTraversalManager
|
||||
DifficultyManager() model.DifficultyManager
|
||||
GHOSTDAGManager() model.GHOSTDAGManager
|
||||
HeaderTipsManager() model.HeaderTipsManager
|
||||
HeaderTipsManager() model.HeadersSelectedTipManager
|
||||
MergeDepthManager() model.MergeDepthManager
|
||||
PastMedianTimeManager() model.PastMedianTimeManager
|
||||
PruningManager() model.PruningManager
|
||||
|
@ -21,22 +21,22 @@ type blockProcessor struct {
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
pastMedianTimeManager model.PastMedianTimeManager
|
||||
coinbaseManager model.CoinbaseManager
|
||||
headerTipsManager model.HeaderTipsManager
|
||||
headerTipsManager model.HeadersSelectedTipManager
|
||||
syncManager model.SyncManager
|
||||
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockStore model.BlockStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
multisetStore model.MultisetStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
pruningStore model.PruningStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
headerTipsStore model.HeaderTipsStore
|
||||
finalityStore model.FinalityStore
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockStore model.BlockStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
multisetStore model.MultisetStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
pruningStore model.PruningStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
headersSelectedTipStore model.HeaderSelectedTipStore
|
||||
finalityStore model.FinalityStore
|
||||
|
||||
stores []model.Store
|
||||
}
|
||||
@ -54,7 +54,7 @@ func New(
|
||||
pastMedianTimeManager model.PastMedianTimeManager,
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
coinbaseManager model.CoinbaseManager,
|
||||
headerTipsManager model.HeaderTipsManager,
|
||||
headerTipsManager model.HeadersSelectedTipManager,
|
||||
syncManager model.SyncManager,
|
||||
|
||||
acceptanceDataStore model.AcceptanceDataStore,
|
||||
@ -68,7 +68,7 @@ func New(
|
||||
reachabilityDataStore model.ReachabilityDataStore,
|
||||
utxoDiffStore model.UTXODiffStore,
|
||||
blockHeaderStore model.BlockHeaderStore,
|
||||
headerTipsStore model.HeaderTipsStore,
|
||||
headersSelectedTipStore model.HeaderSelectedTipStore,
|
||||
finalityStore model.FinalityStore,
|
||||
) model.BlockProcessor {
|
||||
|
||||
@ -86,20 +86,20 @@ func New(
|
||||
headerTipsManager: headerTipsManager,
|
||||
syncManager: syncManager,
|
||||
|
||||
consensusStateManager: consensusStateManager,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockStore: blockStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
multisetStore: multisetStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
consensusStateStore: consensusStateStore,
|
||||
pruningStore: pruningStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
headerTipsStore: headerTipsStore,
|
||||
finalityStore: finalityStore,
|
||||
consensusStateManager: consensusStateManager,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockStore: blockStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
multisetStore: multisetStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
consensusStateStore: consensusStateStore,
|
||||
pruningStore: pruningStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
headersSelectedTipStore: headersSelectedTipStore,
|
||||
finalityStore: finalityStore,
|
||||
|
||||
stores: []model.Store{
|
||||
consensusStateStore,
|
||||
@ -114,7 +114,7 @@ func New(
|
||||
reachabilityDataStore,
|
||||
utxoDiffStore,
|
||||
blockHeaderStore,
|
||||
headerTipsStore,
|
||||
headersSelectedTipStore,
|
||||
finalityStore,
|
||||
},
|
||||
}
|
||||
@ -128,3 +128,10 @@ func (bp *blockProcessor) ValidateAndInsertBlock(block *externalapi.DomainBlock)
|
||||
|
||||
return bp.validateAndInsertBlock(block)
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) ValidateAndInsertPruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateAndInsertPruningPoint")
|
||||
defer onEnd()
|
||||
|
||||
return bp.validateAndInsertPruningPoint(newPruningPoint, serializedUTXOSet)
|
||||
}
|
||||
|
@ -11,36 +11,16 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type insertMode uint8
|
||||
|
||||
const (
|
||||
insertModeGenesis insertMode = iota
|
||||
insertModeHeader
|
||||
insertModeBlockBody
|
||||
insertModeBlock
|
||||
)
|
||||
|
||||
func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock) error {
|
||||
blockHash := consensushashing.HeaderHash(block.Header)
|
||||
log.Debugf("Validating block %s", blockHash)
|
||||
|
||||
insertMode, err := bp.validateAgainstSyncStateAndResolveInsertMode(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bp.checkBlockStatus(blockHash, insertMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bp.validateBlock(block, insertMode)
|
||||
err := bp.validateBlock(block)
|
||||
if err != nil {
|
||||
bp.discardAllChanges()
|
||||
return err
|
||||
}
|
||||
|
||||
if insertMode == insertModeHeader {
|
||||
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
|
||||
if isHeaderOnlyBlock {
|
||||
bp.blockStatusStore.Stage(blockHash, externalapi.StatusHeaderOnly)
|
||||
} else {
|
||||
bp.blockStatusStore.Stage(blockHash, externalapi.StatusUTXOPendingVerification)
|
||||
@ -54,43 +34,38 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
|
||||
}
|
||||
|
||||
var oldHeadersSelectedTip *externalapi.DomainHash
|
||||
if insertMode != insertModeGenesis {
|
||||
isGenesis := *blockHash != *bp.genesisHash
|
||||
if isGenesis {
|
||||
var err error
|
||||
oldHeadersSelectedTip, err = bp.headerTipsManager.SelectedTip()
|
||||
oldHeadersSelectedTip, err = bp.headersSelectedTipStore.HeadersSelectedTip(bp.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if insertMode == insertModeHeader {
|
||||
err = bp.headerTipsManager.AddHeaderTip(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if insertMode == insertModeBlock || insertMode == insertModeGenesis {
|
||||
err = bp.headerTipsManager.AddHeaderTip(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isHeaderOnlyBlock {
|
||||
// Attempt to add the block to the virtual
|
||||
err = bp.consensusStateManager.AddBlockToVirtual(blockHash)
|
||||
err = bp.consensusStateManager.AddBlock(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tips, err := bp.consensusStateStore.Tips(bp.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bp.headerTipsStore.Stage(tips)
|
||||
}
|
||||
|
||||
if insertMode != insertModeGenesis {
|
||||
if isGenesis {
|
||||
err := bp.updateReachabilityReindexRoot(oldHeadersSelectedTip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if insertMode == insertModeBlock {
|
||||
if !isHeaderOnlyBlock {
|
||||
// Trigger pruning, which will check if the pruning point changed and delete the data if it did.
|
||||
err = bp.pruningManager.FindNextPruningPoint()
|
||||
err = bp.pruningManager.UpdatePruningPointByVirtual()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -115,8 +90,8 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
|
||||
logClosureErr = err
|
||||
return fmt.Sprintf("Failed to get sync info: %s", err)
|
||||
}
|
||||
return fmt.Sprintf("New virtual's blue score: %d. Sync state: %s. Block count: %d. Header count: %d",
|
||||
virtualGhostDAGData.BlueScore(), syncInfo.State, syncInfo.BlockCount, syncInfo.HeaderCount)
|
||||
return fmt.Sprintf("New virtual's blue score: %d. Is awaiting UTXO set: %t. Block count: %d. Header count: %d",
|
||||
virtualGhostDAGData.BlueScore(), syncInfo.IsAwaitingUTXOSet, syncInfo.BlockCount, syncInfo.HeaderCount)
|
||||
}))
|
||||
if logClosureErr != nil {
|
||||
return logClosureErr
|
||||
@ -125,64 +100,12 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateAgainstSyncStateAndResolveInsertMode(block *externalapi.DomainBlock) (insertMode, error) {
|
||||
syncInfo, err := bp.syncManager.GetSyncInfo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
syncState := syncInfo.State
|
||||
|
||||
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
|
||||
blockHash := consensushashing.HeaderHash(block.Header)
|
||||
if syncState == externalapi.SyncStateAwaitingGenesis {
|
||||
if isHeaderOnlyBlock {
|
||||
return 0, errors.Errorf("Got a header-only block while awaiting genesis")
|
||||
}
|
||||
if *blockHash != *bp.genesisHash {
|
||||
return 0, errors.Errorf("Received a non-genesis block while awaiting genesis")
|
||||
}
|
||||
return insertModeGenesis, nil
|
||||
}
|
||||
|
||||
if isHeaderOnlyBlock {
|
||||
return insertModeHeader, nil
|
||||
}
|
||||
|
||||
if syncState == externalapi.SyncStateAwaitingUTXOSet {
|
||||
headerTipsPruningPoint, err := bp.consensusStateManager.HeaderTipsPruningPoint()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if *blockHash != *headerTipsPruningPoint {
|
||||
return 0, errors.Errorf("cannot insert blocks other than the header pruning point " +
|
||||
"while awaiting the UTXO set")
|
||||
}
|
||||
return insertModeBlock, nil
|
||||
}
|
||||
|
||||
if syncState == externalapi.SyncStateAwaitingBlockBodies {
|
||||
headerTips, err := bp.headerTipsStore.Tips(bp.databaseContext)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
selectedHeaderTip, err := bp.ghostdagManager.ChooseSelectedParent(headerTips...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if *selectedHeaderTip != *blockHash {
|
||||
return insertModeBlockBody, nil
|
||||
}
|
||||
}
|
||||
|
||||
return insertModeBlock, nil
|
||||
}
|
||||
|
||||
func isHeaderOnlyBlock(block *externalapi.DomainBlock) bool {
|
||||
return len(block.Transactions) == 0
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) updateReachabilityReindexRoot(oldHeadersSelectedTip *externalapi.DomainHash) error {
|
||||
headersSelectedTip, err := bp.headerTipsManager.SelectedTip()
|
||||
headersSelectedTip, err := bp.headersSelectedTipStore.HeadersSelectedTip(bp.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -194,7 +117,9 @@ func (bp *blockProcessor) updateReachabilityReindexRoot(oldHeadersSelectedTip *e
|
||||
return bp.reachabilityManager.UpdateReindexRoot(headersSelectedTip)
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) checkBlockStatus(hash *externalapi.DomainHash, mode insertMode) error {
|
||||
func (bp *blockProcessor) checkBlockStatus(block *externalapi.DomainBlock) error {
|
||||
hash := consensushashing.BlockHash(block)
|
||||
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
|
||||
exists, err := bp.blockStatusStore.Exists(bp.databaseContext, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -212,12 +137,12 @@ func (bp *blockProcessor) checkBlockStatus(hash *externalapi.DomainHash, mode in
|
||||
return errors.Wrapf(ruleerrors.ErrKnownInvalid, "block %s is a known invalid block", hash)
|
||||
}
|
||||
|
||||
isBlockBodyAfterBlockHeader := mode != insertModeHeader && status == externalapi.StatusHeaderOnly
|
||||
isBlockBodyAfterBlockHeader := !isHeaderOnlyBlock && status == externalapi.StatusHeaderOnly
|
||||
if !isBlockBodyAfterBlockHeader {
|
||||
return errors.Wrapf(ruleerrors.ErrDuplicateBlock, "block %s already exists", hash)
|
||||
}
|
||||
|
||||
isDuplicateHeader := mode == insertModeHeader && status == externalapi.StatusHeaderOnly
|
||||
isDuplicateHeader := isHeaderOnlyBlock && status == externalapi.StatusHeaderOnly
|
||||
if isDuplicateHeader {
|
||||
return errors.Wrapf(ruleerrors.ErrDuplicateBlock, "block %s already exists", hash)
|
||||
}
|
||||
@ -225,57 +150,16 @@ func (bp *blockProcessor) checkBlockStatus(hash *externalapi.DomainHash, mode in
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock, mode insertMode) error {
|
||||
blockHash := consensushashing.HeaderHash(block.Header)
|
||||
hasHeader, err := bp.hasHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasHeader {
|
||||
bp.blockHeaderStore.Stage(blockHash, block.Header)
|
||||
}
|
||||
|
||||
// If any validation until (included) proof-of-work fails, simply
|
||||
// return an error without writing anything in the database.
|
||||
// This is to prevent spamming attacks.
|
||||
err = bp.validatePreProofOfWork(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bp.validatePruningPointViolationAndProofOfWorkAndDifficulty(block, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If in-context validations fail, discard all changes and store the
|
||||
// block with StatusInvalid.
|
||||
err = bp.validatePostProofOfWork(block, mode)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
bp.discardAllChanges()
|
||||
hash := consensushashing.BlockHash(block)
|
||||
bp.blockStatusStore.Stage(hash, externalapi.StatusInvalid)
|
||||
commitErr := bp.commitAllChanges()
|
||||
if commitErr != nil {
|
||||
return commitErr
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validatePreProofOfWork(block *externalapi.DomainBlock) error {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
|
||||
hasHeader, err := bp.hasHeader(blockHash)
|
||||
hasValidatedOnlyHeader, err := bp.hasValidatedOnlyHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hasHeader {
|
||||
if hasValidatedOnlyHeader {
|
||||
log.Debugf("Block %s header was already validated, so skip the rest of validatePreProofOfWork", blockHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -286,41 +170,43 @@ func (bp *blockProcessor) validatePreProofOfWork(block *externalapi.DomainBlock)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validatePruningPointViolationAndProofOfWorkAndDifficulty(block *externalapi.DomainBlock, mode insertMode) error {
|
||||
blockHash := consensushashing.HeaderHash(block.Header)
|
||||
if mode != insertModeHeader {
|
||||
// We stage the block here since we need it for parent validation
|
||||
bp.blockStore.Stage(blockHash, block)
|
||||
}
|
||||
return bp.blockValidator.ValidatePruningPointViolationAndProofOfWorkAndDifficulty(blockHash)
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock, mode insertMode) error {
|
||||
func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock) error {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
|
||||
if mode != insertModeHeader {
|
||||
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
|
||||
if !isHeaderOnlyBlock {
|
||||
bp.blockStore.Stage(blockHash, block)
|
||||
err := bp.blockValidator.ValidateBodyInIsolation(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hasHeader, err := bp.hasHeader(blockHash)
|
||||
hasValidatedHeader, err := bp.hasValidatedOnlyHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasHeader {
|
||||
if !hasValidatedHeader {
|
||||
err = bp.blockValidator.ValidateHeaderInContext(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !isHeaderOnlyBlock {
|
||||
err = bp.blockValidator.ValidateBodyInContext(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Tracef("Skipping ValidateBodyInContext for block %s because it's header only", blockHash)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) hasHeader(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
func (bp *blockProcessor) hasValidatedOnlyHeader(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
exists, err := bp.blockStatusStore.Exists(bp.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -0,0 +1,38 @@
|
||||
package blockprocessor
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (bp *blockProcessor) validateAndInsertPruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
|
||||
log.Info("Checking that the given pruning point is the expected pruning point")
|
||||
|
||||
expectedNewPruningPointHash, err := bp.pruningManager.CalculatePruningPointByHeaderSelectedTip()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newPruningPointHash := consensushashing.BlockHash(newPruningPoint)
|
||||
|
||||
if *expectedNewPruningPointHash != *newPruningPointHash {
|
||||
return errors.Wrapf(ruleerrors.ErrUnexpectedPruningPoint, "expected pruning point %s but got %s",
|
||||
expectedNewPruningPointHash, newPruningPointHash)
|
||||
}
|
||||
|
||||
// We have to validate the pruning point block before we set the new pruning point in consensusStateManager.
|
||||
log.Infof("Validating the new pruning point %s", newPruningPointHash)
|
||||
err = bp.validateBlockAndDiscardChanges(newPruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bp.consensusStateManager.UpdatePruningPoint(newPruningPoint, serializedUTXOSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bp.ValidateAndInsertBlock(newPruningPoint)
|
||||
}
|
67
domain/consensus/processes/blockprocessor/validateblock.go
Normal file
67
domain/consensus/processes/blockprocessor/validateblock.go
Normal file
@ -0,0 +1,67 @@
|
||||
package blockprocessor
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (bp *blockProcessor) validateBlockAndDiscardChanges(block *externalapi.DomainBlock) error {
|
||||
defer bp.discardAllChanges()
|
||||
return bp.validateBlock(block)
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock) error {
|
||||
blockHash := consensushashing.HeaderHash(block.Header)
|
||||
log.Debugf("Validating block %s", blockHash)
|
||||
|
||||
err := bp.checkBlockStatus(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasValidatedHeader, err := bp.hasValidatedOnlyHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasValidatedHeader {
|
||||
log.Tracef("Staging block %s header", blockHash)
|
||||
bp.blockHeaderStore.Stage(blockHash, block.Header)
|
||||
} else {
|
||||
log.Tracef("Block %s header is already known, so no need to stage it", blockHash)
|
||||
}
|
||||
|
||||
// If any validation until (included) proof-of-work fails, simply
|
||||
// return an error without writing anything in the database.
|
||||
// This is to prevent spamming attacks.
|
||||
err = bp.validatePreProofOfWork(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasValidatedHeader {
|
||||
err = bp.blockValidator.ValidatePruningPointViolationAndProofOfWorkAndDifficulty(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If in-context validations fail, discard all changes and store the
|
||||
// block with StatusInvalid.
|
||||
err = bp.validatePostProofOfWork(block)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
bp.discardAllChanges()
|
||||
hash := consensushashing.BlockHash(block)
|
||||
bp.blockStatusStore.Stage(hash, externalapi.StatusInvalid)
|
||||
commitErr := bp.commitAllChanges()
|
||||
if commitErr != nil {
|
||||
return commitErr
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package blockvalidator
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"math"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@ -13,6 +14,9 @@ import (
|
||||
// ValidateBodyInContext validates block bodies in the context of the current
|
||||
// consensus state
|
||||
func (v *blockValidator) ValidateBodyInContext(blockHash *externalapi.DomainHash) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateBodyInContext")
|
||||
defer onEnd()
|
||||
|
||||
return v.checkBlockTransactionsFinalized(blockHash)
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,16 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ValidateBodyInIsolation validates block bodies in isolation from the current
|
||||
// consensus state
|
||||
func (v *blockValidator) ValidateBodyInIsolation(blockHash *externalapi.DomainHash) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateBodyInContext")
|
||||
defer onEnd()
|
||||
|
||||
block, err := v.blockStore.Block(v.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -4,23 +4,27 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ValidateHeaderInContext validates block headers in the context of the current
|
||||
// consensus state
|
||||
func (v *blockValidator) ValidateHeaderInContext(blockHash *externalapi.DomainHash) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateHeaderInContext")
|
||||
defer onEnd()
|
||||
|
||||
header, err := v.blockHeaderStore.BlockHeader(v.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isHeadersOnlyBlock, err := v.isHeadersOnlyBlock(blockHash)
|
||||
hasValidatedHeader, err := v.hasValidatedHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isHeadersOnlyBlock {
|
||||
if !hasValidatedHeader {
|
||||
err = v.ghostdagManager.GHOSTDAG(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -60,7 +64,7 @@ func (v *blockValidator) ValidateHeaderInContext(blockHash *externalapi.DomainHa
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *blockValidator) isHeadersOnlyBlock(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
func (v *blockValidator) hasValidatedHeader(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
exists, err := v.blockStatusStore.Exists(v.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -11,6 +12,9 @@ import (
|
||||
// ValidateHeaderInIsolation validates block headers in isolation from the current
|
||||
// consensus state
|
||||
func (v *blockValidator) ValidateHeaderInIsolation(blockHash *externalapi.DomainHash) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateHeaderInIsolation")
|
||||
defer onEnd()
|
||||
|
||||
header, err := v.blockHeaderStore.BlockHeader(v.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
|
7
domain/consensus/processes/blockvalidator/log.go
Normal file
7
domain/consensus/processes/blockvalidator/log.go
Normal file
@ -0,0 +1,7 @@
|
||||
package blockvalidator
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.BLVL)
|
@ -5,11 +5,15 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (v *blockValidator) ValidatePruningPointViolationAndProofOfWorkAndDifficulty(blockHash *externalapi.DomainHash) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidatePruningPointViolationAndProofOfWorkAndDifficulty")
|
||||
defer onEnd()
|
||||
|
||||
header, err := v.blockHeaderStore.BlockHeader(v.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -102,7 +106,7 @@ func (v *blockValidator) checkProofOfWork(header *externalapi.DomainBlockHeader)
|
||||
func (v *blockValidator) checkParentsExist(blockHash *externalapi.DomainHash, header *externalapi.DomainBlockHeader) error {
|
||||
missingParentHashes := []*externalapi.DomainHash{}
|
||||
|
||||
isFullBlock, err := v.blockStore.HasBlock(v.databaseContext, blockHash)
|
||||
hasBlockBody, err := v.blockStore.HasBlock(v.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -117,12 +121,31 @@ func (v *blockValidator) checkParentsExist(blockHash *externalapi.DomainHash, he
|
||||
continue
|
||||
}
|
||||
|
||||
if isFullBlock {
|
||||
parentStatus, err := v.blockStatusStore.Get(v.databaseContext, parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentStatus, err := v.blockStatusStore.Get(v.databaseContext, parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parentStatus == externalapi.StatusInvalid {
|
||||
return errors.Wrapf(ruleerrors.ErrInvalidAncestorBlock, "parent %s is invalid", parent)
|
||||
}
|
||||
|
||||
if hasBlockBody {
|
||||
if parentStatus == externalapi.StatusHeaderOnly {
|
||||
pruningPoint, err := v.pruningStore.PruningPoint(v.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isInPastOfPruningPoint, err := v.dagTopologyManager.IsAncestorOf(parent, pruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInPastOfPruningPoint {
|
||||
continue
|
||||
}
|
||||
|
||||
missingParentHashes = append(missingParentHashes, parent)
|
||||
}
|
||||
}
|
||||
|
@ -8,41 +8,41 @@ import (
|
||||
// AddBlockToVirtual submits the given block to be added to the
|
||||
// current virtual. This process may result in a new virtual block
|
||||
// getting created
|
||||
func (csm *consensusStateManager) AddBlockToVirtual(blockHash *externalapi.DomainHash) error {
|
||||
log.Tracef("AddBlockToVirtual start for block %s", blockHash)
|
||||
defer log.Tracef("AddBlockToVirtual end for block %s", blockHash)
|
||||
func (csm *consensusStateManager) AddBlock(blockHash *externalapi.DomainHash) error {
|
||||
log.Tracef("AddBlock start for block %s", blockHash)
|
||||
defer log.Tracef("AddBlock end for block %s", blockHash)
|
||||
|
||||
log.Tracef("Resolving whether the block %s is the next virtual selected parent", blockHash)
|
||||
isNextVirtualSelectedParent, err := csm.isNextVirtualSelectedParent(blockHash)
|
||||
isCandidateToBeNextVirtualSelectedParent, err := csm.isCandidateToBeNextVirtualSelectedParent(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isNextVirtualSelectedParent {
|
||||
log.Tracef("Block %s is the new virtual. Resolving its block status", blockHash)
|
||||
blockStatus, err := csm.resolveBlockStatus(blockHash)
|
||||
if isCandidateToBeNextVirtualSelectedParent {
|
||||
// It's important to check for finality violation before resolving the block status, because the status of
|
||||
// blocks with a selected chain that doesn't contain the pruning point cannot be resolved because they will
|
||||
// eventually try to fetch UTXO diffs from the past of the pruning point.
|
||||
log.Tracef("Block %s is candidate to be the next virtual selected parent. Resolving whether it violates "+
|
||||
"finality", blockHash)
|
||||
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if blockStatus == externalapi.StatusValid {
|
||||
log.Tracef("Block %s is tentatively valid. Resolving whether it violates finality", blockHash)
|
||||
err = csm.checkFinalityViolation(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Re-fetch the block status for logging purposes
|
||||
// because it could've been changed in
|
||||
// checkFinalityViolation
|
||||
blockStatus, err = csm.blockStatusStore.Get(csm.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if shouldNotify {
|
||||
//TODO: Send finality conflict notification
|
||||
log.Warnf("Finality Violation Detected! Block %s violates finality!", blockHash)
|
||||
}
|
||||
|
||||
log.Debugf("Block %s is the next virtual selected parent. "+
|
||||
"Its resolved status is `%s`", blockHash, blockStatus)
|
||||
if !isViolatingFinality {
|
||||
log.Tracef("Block %s doesn't violate finality. Resolving its block status", blockHash)
|
||||
blockStatus, err := csm.resolveBlockStatus(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Block %s resolved to status `%s`", blockHash, blockStatus)
|
||||
}
|
||||
} else {
|
||||
log.Debugf("Block %s is not the next virtual selected parent, "+
|
||||
"therefore its status remains `%s`", blockHash, externalapi.StatusUTXOPendingVerification)
|
||||
@ -64,9 +64,9 @@ func (csm *consensusStateManager) AddBlockToVirtual(blockHash *externalapi.Domai
|
||||
return nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) isNextVirtualSelectedParent(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
log.Tracef("isNextVirtualSelectedParent start for block %s", blockHash)
|
||||
defer log.Tracef("isNextVirtualSelectedParent end for block %s", blockHash)
|
||||
func (csm *consensusStateManager) isCandidateToBeNextVirtualSelectedParent(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
log.Tracef("isCandidateToBeNextVirtualSelectedParent start for block %s", blockHash)
|
||||
defer log.Tracef("isCandidateToBeNextVirtualSelectedParent end for block %s", blockHash)
|
||||
|
||||
if *blockHash == *csm.genesisHash {
|
||||
log.Tracef("Block %s is the genesis block, therefore it is "+
|
||||
|
@ -2,23 +2,65 @@ package consensusstatemanager
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
func (csm *consensusStateManager) checkFinalityViolation(
|
||||
blockHash *externalapi.DomainHash) error {
|
||||
func (csm *consensusStateManager) isViolatingFinality(blockHash *externalapi.DomainHash) (isViolatingFinality bool,
|
||||
shouldSendNotification bool, err error) {
|
||||
|
||||
log.Tracef("checkFinalityViolation start for block %s", blockHash)
|
||||
defer log.Tracef("checkFinalityViolation end for block %s", blockHash)
|
||||
log.Tracef("isViolatingFinality start for block %s", blockHash)
|
||||
defer log.Tracef("isViolatingFinality end for block %s", blockHash)
|
||||
|
||||
isViolatingFinality, err := csm.finalityManager.IsViolatingFinality(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
if *blockHash == *csm.genesisHash {
|
||||
log.Tracef("Block %s is the genesis block, "+
|
||||
"and does not violate finality by definition", blockHash)
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
if isViolatingFinality {
|
||||
csm.blockStatusStore.Stage(blockHash, externalapi.StatusUTXOPendingVerification)
|
||||
log.Warnf("Finality Violation Detected! Block %s violates finality!", blockHash)
|
||||
return nil
|
||||
var finalityPoint *externalapi.DomainHash
|
||||
virtualFinalityPoint, err := csm.finalityManager.VirtualFinalityPoint()
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
log.Tracef("The virtual finality point is: %s", virtualFinalityPoint)
|
||||
|
||||
// There can be a situation where the virtual points close to the pruning point (or even in the past
|
||||
// of the pruning point before calling validateAndInsertBlock for the pruning point block) and the
|
||||
// finality point from the virtual point-of-view is in the past of the pruning point.
|
||||
// In such situation we override the finality point to be the pruning point to avoid situations where
|
||||
// the virtual selected parent chain don't include the pruning point.
|
||||
pruningPoint, err := csm.pruningStore.PruningPoint(csm.databaseContext)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
log.Tracef("The pruning point is: %s", pruningPoint)
|
||||
|
||||
isFinalityPointInPastOfPruningPoint, err := csm.dagTopologyManager.IsAncestorOf(virtualFinalityPoint, pruningPoint)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
if !isFinalityPointInPastOfPruningPoint {
|
||||
finalityPoint = virtualFinalityPoint
|
||||
} else {
|
||||
log.Tracef("The virtual finality point is %s in the past of the pruning point, so finality is validated "+
|
||||
"using the pruning point", virtualFinalityPoint)
|
||||
finalityPoint = pruningPoint
|
||||
}
|
||||
|
||||
isInSelectedParentChainOfFinalityPoint, err := csm.dagTopologyManager.IsInSelectedParentChainOf(finalityPoint,
|
||||
blockHash)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
if !isInSelectedParentChainOfFinalityPoint {
|
||||
if !isFinalityPointInPastOfPruningPoint {
|
||||
return true, true, nil
|
||||
}
|
||||
// On IBD it's pretty normal to get blocks in the anticone of the pruning
|
||||
// point, so we don't notify on cases when the pruning point is in the future
|
||||
// of the finality point.
|
||||
return true, false, nil
|
||||
}
|
||||
log.Tracef("Block %s does not violate finality", blockHash)
|
||||
|
||||
return nil
|
||||
return false, false, nil
|
||||
}
|
||||
|
@ -25,16 +25,17 @@ type consensusStateManager struct {
|
||||
mergeDepthManager model.MergeDepthManager
|
||||
finalityManager model.FinalityManager
|
||||
|
||||
headerTipsStore model.HeaderTipsStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
multisetStore model.MultisetStore
|
||||
blockStore model.BlockStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
headersSelectedTipStore model.HeaderSelectedTipStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
multisetStore model.MultisetStore
|
||||
blockStore model.BlockStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
pruningStore model.PruningStore
|
||||
|
||||
stores []model.Store
|
||||
}
|
||||
@ -68,7 +69,8 @@ func New(
|
||||
blockRelationStore model.BlockRelationStore,
|
||||
acceptanceDataStore model.AcceptanceDataStore,
|
||||
blockHeaderStore model.BlockHeaderStore,
|
||||
headerTipsStore model.HeaderTipsStore) (model.ConsensusStateManager, error) {
|
||||
headersSelectedTipStore model.HeaderSelectedTipStore,
|
||||
pruningStore model.PruningStore) (model.ConsensusStateManager, error) {
|
||||
|
||||
csm := &consensusStateManager{
|
||||
pruningDepth: pruningDepth,
|
||||
@ -89,16 +91,17 @@ func New(
|
||||
mergeDepthManager: mergeDepthManager,
|
||||
finalityManager: finalityManager,
|
||||
|
||||
multisetStore: multisetStore,
|
||||
blockStore: blockStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
consensusStateStore: consensusStateStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
headerTipsStore: headerTipsStore,
|
||||
multisetStore: multisetStore,
|
||||
blockStore: blockStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
consensusStateStore: consensusStateStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
headersSelectedTipStore: headersSelectedTipStore,
|
||||
pruningStore: pruningStore,
|
||||
|
||||
stores: []model.Store{
|
||||
consensusStateStore,
|
||||
@ -111,7 +114,8 @@ func New(
|
||||
consensusStateStore,
|
||||
utxoDiffStore,
|
||||
blockHeaderStore,
|
||||
headerTipsStore,
|
||||
headersSelectedTipStore,
|
||||
pruningStore,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/serialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization"
|
||||
@ -12,18 +13,11 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var virtualHeaderHash = &externalapi.DomainHash{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) SetPruningPointUTXOSet(serializedUTXOSet []byte) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "SetPruningPointUTXOSet")
|
||||
func (csm *consensusStateManager) UpdatePruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "UpdatePruningPoint")
|
||||
defer onEnd()
|
||||
|
||||
err := csm.setPruningPointUTXOSet(serializedUTXOSet)
|
||||
err := csm.updatePruningPoint(newPruningPoint, serializedUTXOSet)
|
||||
if err != nil {
|
||||
csm.discardSetPruningPointUTXOSetChanges()
|
||||
return err
|
||||
@ -32,15 +26,23 @@ func (csm *consensusStateManager) SetPruningPointUTXOSet(serializedUTXOSet []byt
|
||||
return csm.commitSetPruningPointUTXOSetAll()
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byte) error {
|
||||
log.Tracef("setPruningPointUTXOSet start")
|
||||
defer log.Tracef("setPruningPointUTXOSet end")
|
||||
func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
|
||||
log.Tracef("updatePruningPoint start")
|
||||
defer log.Tracef("updatePruningPoint end")
|
||||
|
||||
headerTipsPruningPoint, err := csm.HeaderTipsPruningPoint()
|
||||
newPruningPointHash := consensushashing.BlockHash(newPruningPoint)
|
||||
|
||||
// We ignore the shouldSendNotification return value because we always want to send finality conflict notification
|
||||
// in case the new pruning point violates finality
|
||||
isViolatingFinality, _, err := csm.isViolatingFinality(newPruningPointHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Tracef("The pruning point of the header tips is: %s", headerTipsPruningPoint)
|
||||
|
||||
if isViolatingFinality {
|
||||
log.Warnf("Finality Violation Detected! The suggest pruning point %s violates finality!", newPruningPointHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
protoUTXOSet := &utxoserialization.ProtoUTXOSet{}
|
||||
err = proto.Unmarshal(serializedUTXOSet, protoUTXOSet)
|
||||
@ -54,24 +56,24 @@ func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byt
|
||||
}
|
||||
log.Tracef("Calculated multiset for given UTXO set: %s", utxoSetMultiSet.Hash())
|
||||
|
||||
headerTipsPruningPointHeader, err := csm.blockHeaderStore.BlockHeader(csm.databaseContext, headerTipsPruningPoint)
|
||||
newPruningPointHeader, err := csm.blockHeaderStore.BlockHeader(csm.databaseContext, newPruningPointHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Tracef("The multiset in the header of the header tip pruning point: %s",
|
||||
headerTipsPruningPointHeader.UTXOCommitment)
|
||||
log.Tracef("The UTXO commitment of the pruning point: %s",
|
||||
newPruningPointHeader.UTXOCommitment)
|
||||
|
||||
if headerTipsPruningPointHeader.UTXOCommitment != *utxoSetMultiSet.Hash() {
|
||||
if newPruningPointHeader.UTXOCommitment != *utxoSetMultiSet.Hash() {
|
||||
return errors.Wrapf(ruleerrors.ErrBadPruningPointUTXOSet, "the expected multiset hash of the pruning "+
|
||||
"point UTXO set is %s but got %s", headerTipsPruningPointHeader.UTXOCommitment, *utxoSetMultiSet.Hash())
|
||||
"point UTXO set is %s but got %s", newPruningPointHeader.UTXOCommitment, *utxoSetMultiSet.Hash())
|
||||
}
|
||||
log.Tracef("Header tip pruning point multiset validation passed")
|
||||
log.Tracef("The new pruning point UTXO commitment validation passed")
|
||||
|
||||
log.Tracef("Staging the parent hashes for the header tips pruning point as the DAG tips")
|
||||
csm.consensusStateStore.StageTips(headerTipsPruningPointHeader.ParentHashes)
|
||||
log.Tracef("Staging the parent hashes for pruning point as the DAG tips")
|
||||
csm.consensusStateStore.StageTips(newPruningPointHeader.ParentHashes)
|
||||
|
||||
log.Tracef("Setting the parent hashes for the header tips pruning point as the virtual parents")
|
||||
err = csm.dagTopologyManager.SetParents(model.VirtualBlockHash, headerTipsPruningPointHeader.ParentHashes)
|
||||
err = csm.dagTopologyManager.SetParents(model.VirtualBlockHash, newPruningPointHeader.ParentHashes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -82,6 +84,13 @@ func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byt
|
||||
return err
|
||||
}
|
||||
|
||||
// Before we manually mark the new pruning point as valid, we validate that all of its transactions are valid
|
||||
// against the provided UTXO set.
|
||||
err = csm.validateBlockTransactionsAgainstPastUTXO(newPruningPoint, utxo.NewUTXODiff())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = csm.ghostdagManager.GHOSTDAG(model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -93,8 +102,11 @@ func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byt
|
||||
return err
|
||||
}
|
||||
|
||||
log.Tracef("Staging the status of the header tips pruning point as %s", externalapi.StatusValid)
|
||||
csm.blockStatusStore.Stage(headerTipsPruningPoint, externalapi.StatusValid)
|
||||
log.Tracef("Staging the new pruning point and its UTXO set")
|
||||
csm.pruningStore.Stage(newPruningPointHash, serializedUTXOSet)
|
||||
|
||||
log.Tracef("Staging the new pruning point as %s", externalapi.StatusValid)
|
||||
csm.blockStatusStore.Stage(newPruningPointHash, externalapi.StatusValid)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -145,34 +157,3 @@ func (p protoUTXOSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoE
|
||||
func protoUTXOSetToReadOnlyUTXOSetIterator(protoUTXOSet *utxoserialization.ProtoUTXOSet) model.ReadOnlyUTXOSetIterator {
|
||||
return &protoUTXOSetIterator{utxoSet: protoUTXOSet}
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) HeaderTipsPruningPoint() (*externalapi.DomainHash, error) {
|
||||
log.Tracef("HeaderTipsPruningPoint start")
|
||||
defer log.Tracef("HeaderTipsPruningPoint end")
|
||||
|
||||
headerTips, err := csm.headerTipsStore.Tips(csm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Tracef("The current header tips are: %s", headerTips)
|
||||
|
||||
log.Tracef("Temporarily staging the parents of the virtual header to be the header tips: %s", headerTips)
|
||||
csm.blockRelationStore.StageBlockRelation(virtualHeaderHash, &model.BlockRelations{
|
||||
Parents: headerTips,
|
||||
})
|
||||
|
||||
defer csm.blockRelationStore.Discard()
|
||||
|
||||
err = csm.ghostdagManager.GHOSTDAG(virtualHeaderHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer csm.ghostdagDataStore.Discard()
|
||||
|
||||
pruningPoint, err := csm.dagTraversalManager.BlockAtDepth(virtualHeaderHash, csm.pruningDepth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Tracef("The block at depth %d from %s is: %s", csm.pruningDepth, virtualHeaderHash, pruningPoint)
|
||||
return pruningPoint, nil
|
||||
}
|
@ -47,7 +47,7 @@ func (csm *consensusStateManager) verifyUTXO(block *externalapi.DomainBlock, blo
|
||||
log.Tracef("Coinbase transaction validation passed for block %s", blockHash)
|
||||
|
||||
log.Tracef("Validating transactions against past UTXO for block %s", blockHash)
|
||||
err = csm.validateBlockTransactionsAgainstPastUTXO(block, blockHash, pastUTXODiff)
|
||||
err = csm.validateBlockTransactionsAgainstPastUTXO(block, pastUTXODiff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -57,8 +57,9 @@ func (csm *consensusStateManager) verifyUTXO(block *externalapi.DomainBlock, blo
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) validateBlockTransactionsAgainstPastUTXO(block *externalapi.DomainBlock,
|
||||
blockHash *externalapi.DomainHash, pastUTXODiff model.UTXODiff) error {
|
||||
pastUTXODiff model.UTXODiff) error {
|
||||
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
log.Tracef("validateBlockTransactionsAgainstPastUTXO start for block %s", blockHash)
|
||||
defer log.Tracef("validateBlockTransactionsAgainstPastUTXO end for block %s", blockHash)
|
||||
|
||||
|
@ -2,9 +2,9 @@ package dagtraversalmanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// dagTraversalManager exposes methods for travering blocks
|
||||
@ -100,6 +100,11 @@ func (dtm *dagTraversalManager) LowestChainBlockAboveOrEqualToBlueScore(highHash
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if highBlockGHOSTDAGData.BlueScore() < blueScore {
|
||||
return nil, errors.Errorf("the given blue score %d is higher than block %s blue score of %d",
|
||||
blueScore, highHash, highBlockGHOSTDAGData.BlueScore())
|
||||
}
|
||||
|
||||
currentHash := highHash
|
||||
currentBlockGHOSTDAGData := highBlockGHOSTDAGData
|
||||
iterator := dtm.SelectedParentIterator(highHash)
|
||||
@ -112,7 +117,7 @@ func (dtm *dagTraversalManager) LowestChainBlockAboveOrEqualToBlueScore(highHash
|
||||
if selectedParentBlockGHOSTDAGData.BlueScore() < blueScore {
|
||||
break
|
||||
}
|
||||
currentHash = selectedParentBlockGHOSTDAGData.SelectedParent()
|
||||
currentHash = currentBlockGHOSTDAGData.SelectedParent()
|
||||
currentBlockGHOSTDAGData = selectedParentBlockGHOSTDAGData
|
||||
}
|
||||
|
||||
|
@ -35,31 +35,6 @@ func New(databaseContext model.DBReader,
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
|
@ -0,0 +1,55 @@
|
||||
package headersselectedtipmanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
type headerTipsManager struct {
|
||||
databaseContext model.DBReader
|
||||
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
headersSelectedTipStore model.HeaderSelectedTipStore
|
||||
}
|
||||
|
||||
// New instantiates a new HeadersSelectedTipManager
|
||||
func New(databaseContext model.DBReader,
|
||||
dagTopologyManager model.DAGTopologyManager,
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
headersSelectedTipStore model.HeaderSelectedTipStore) model.HeadersSelectedTipManager {
|
||||
|
||||
return &headerTipsManager{
|
||||
databaseContext: databaseContext,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
ghostdagManager: ghostdagManager,
|
||||
headersSelectedTipStore: headersSelectedTipStore,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *headerTipsManager) AddHeaderTip(hash *externalapi.DomainHash) error {
|
||||
hasSelectedTip, err := h.headersSelectedTipStore.Has(h.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasSelectedTip {
|
||||
h.headersSelectedTipStore.Stage(hash)
|
||||
} else {
|
||||
headersSelectedTip, err := h.headersSelectedTipStore.HeadersSelectedTip(h.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newHeadersSelectedTip, err := h.ghostdagManager.ChooseSelectedParent(headersSelectedTip, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *newHeadersSelectedTip != *headersSelectedTip {
|
||||
h.headersSelectedTipStore.Stage(newHeadersSelectedTip)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package headertipsmanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
type headerTipsManager struct {
|
||||
databaseContext model.DBReader
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
headerTipsStore model.HeaderTipsStore
|
||||
}
|
||||
|
||||
// New instantiates a new HeaderTipsManager
|
||||
func New(databaseContext model.DBReader,
|
||||
dagTopologyManager model.DAGTopologyManager,
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
headerTipsStore model.HeaderTipsStore) model.HeaderTipsManager {
|
||||
return &headerTipsManager{
|
||||
databaseContext: databaseContext,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
ghostdagManager: ghostdagManager,
|
||||
headerTipsStore: headerTipsStore,
|
||||
}
|
||||
}
|
||||
|
||||
func (h headerTipsManager) AddHeaderTip(hash *externalapi.DomainHash) error {
|
||||
tips := []*externalapi.DomainHash{}
|
||||
hasTips, err := h.headerTipsStore.HasTips(h.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hasTips {
|
||||
var err error
|
||||
tips, err = h.headerTipsStore.Tips(h.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newTips := make([]*externalapi.DomainHash, 0, len(tips)+1)
|
||||
for _, tip := range tips {
|
||||
isAncestorOf, err := h.dagTopologyManager.IsAncestorOf(tip, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isAncestorOf {
|
||||
newTips = append(newTips, tip)
|
||||
}
|
||||
}
|
||||
|
||||
newTips = append(newTips, hash)
|
||||
h.headerTipsStore.Stage(newTips)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package headertipsmanager
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
func (h headerTipsManager) SelectedTip() (*externalapi.DomainHash, error) {
|
||||
tips, err := h.headerTipsStore.Tips(h.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectedTip, err := h.ghostdagManager.ChooseSelectedParent(tips...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return selectedTip, nil
|
||||
}
|
5
domain/consensus/processes/pruningmanager/log.go
Normal file
5
domain/consensus/processes/pruningmanager/log.go
Normal file
@ -0,0 +1,5 @@
|
||||
package pruningmanager
|
||||
|
||||
import "github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PRNM)
|
@ -11,13 +11,14 @@ import (
|
||||
type pruningManager struct {
|
||||
databaseContext model.DBReader
|
||||
|
||||
dagTraversalManager model.DAGTraversalManager
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
consensusStateManager model.ConsensusStateManager
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
pruningStore model.PruningStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
dagTraversalManager model.DAGTraversalManager
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
consensusStateManager model.ConsensusStateManager
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
pruningStore model.PruningStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
headerSelectedTipStore model.HeaderSelectedTipStore
|
||||
|
||||
multiSetStore model.MultisetStore
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
@ -40,6 +41,7 @@ func New(
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
pruningStore model.PruningStore,
|
||||
blockStatusStore model.BlockStatusStore,
|
||||
headerSelectedTipStore model.HeaderSelectedTipStore,
|
||||
|
||||
multiSetStore model.MultisetStore,
|
||||
acceptanceDataStore model.AcceptanceDataStore,
|
||||
@ -52,27 +54,28 @@ func New(
|
||||
) model.PruningManager {
|
||||
|
||||
return &pruningManager{
|
||||
databaseContext: databaseContext,
|
||||
dagTraversalManager: dagTraversalManager,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
consensusStateManager: consensusStateManager,
|
||||
consensusStateStore: consensusStateStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
pruningStore: pruningStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
multiSetStore: multiSetStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blocksStore: blocksStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
genesisHash: genesisHash,
|
||||
pruningDepth: pruningDepth,
|
||||
finalityInterval: finalityInterval,
|
||||
databaseContext: databaseContext,
|
||||
dagTraversalManager: dagTraversalManager,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
consensusStateManager: consensusStateManager,
|
||||
consensusStateStore: consensusStateStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
pruningStore: pruningStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
multiSetStore: multiSetStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blocksStore: blocksStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
headerSelectedTipStore: headerSelectedTipStore,
|
||||
genesisHash: genesisHash,
|
||||
pruningDepth: pruningDepth,
|
||||
finalityInterval: finalityInterval,
|
||||
}
|
||||
}
|
||||
|
||||
// FindNextPruningPoint finds the next pruning point from the
|
||||
// given blockHash
|
||||
func (pm *pruningManager) FindNextPruningPoint() error {
|
||||
func (pm *pruningManager) UpdatePruningPointByVirtual() error {
|
||||
hasPruningPoint, err := pm.pruningStore.HasPruningPoint(pm.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -95,40 +98,41 @@ func (pm *pruningManager) FindNextPruningPoint() error {
|
||||
return err
|
||||
}
|
||||
|
||||
virtualSelectedParent, err := pm.ghostdagDataStore.Get(pm.databaseContext, virtual.SelectedParent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentPGhost, err := pm.ghostdagDataStore.Get(pm.databaseContext, currentP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentPBlueScore := currentPGhost.BlueScore()
|
||||
// Because the pruning point changes only once per finality, then there's no need to even check for that if a finality interval hasn't passed.
|
||||
if virtual.BlueScore() <= currentPBlueScore+pm.finalityInterval {
|
||||
if virtualSelectedParent.BlueScore() <= currentPBlueScore+pm.finalityInterval {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This means the pruning point is still genesis.
|
||||
if virtual.BlueScore() <= pm.pruningDepth+pm.finalityInterval {
|
||||
if virtualSelectedParent.BlueScore() <= pm.pruningDepth+pm.finalityInterval {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get Virtual(pruningDepth)
|
||||
candidatePHash, err := pm.dagTraversalManager.BlockAtDepth(model.VirtualBlockHash, pm.pruningDepth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
candidatePGhost, err := pm.ghostdagDataStore.Get(pm.databaseContext, candidatePHash)
|
||||
newPruningPoint, err := pm.calculatePruningPointFromBlock(model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Actually check if the pruning point changed
|
||||
if (currentPBlueScore / pm.finalityInterval) < (candidatePGhost.BlueScore() / pm.finalityInterval) {
|
||||
err = pm.savePruningPoint(candidatePHash)
|
||||
if *newPruningPoint != *currentP {
|
||||
err = pm.savePruningPoint(newPruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pm.deletePastBlocks(candidatePHash)
|
||||
return pm.deletePastBlocks(newPruningPoint)
|
||||
}
|
||||
return pm.deletePastBlocks(currentP)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) deletePastBlocks(pruningPoint *externalapi.DomainHash) error {
|
||||
@ -233,6 +237,30 @@ func (pm *pruningManager) deleteBlock(blockHash *externalapi.DomainHash) (alread
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) CalculatePruningPointByHeaderSelectedTip() (*externalapi.DomainHash, error) {
|
||||
headersSelectedTip, err := pm.headerSelectedTipStore.HeadersSelectedTip(pm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pm.calculatePruningPointFromBlock(headersSelectedTip)
|
||||
}
|
||||
|
||||
func (pm *pruningManager) calculatePruningPointFromBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
|
||||
ghostdagData, err := pm.ghostdagDataStore.Get(pm.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
targetBlueScore := uint64(0)
|
||||
if ghostdagData.BlueScore() > pm.pruningDepth {
|
||||
// The target blue is calculated by calculating ghostdagData.BlueScore() - pm.pruningDepth and rounding
|
||||
// down with the precision of finality interval.
|
||||
targetBlueScore = ((ghostdagData.BlueScore() - pm.pruningDepth) / pm.finalityInterval) * pm.finalityInterval
|
||||
}
|
||||
return pm.dagTraversalManager.LowestChainBlockAboveOrEqualToBlueScore(blockHash, targetBlueScore)
|
||||
}
|
||||
|
||||
func serializeUTXOSetIterator(iter model.ReadOnlyUTXOSetIterator) ([]byte, error) {
|
||||
serializedUtxo, err := utxoserialization.ReadOnlyUTXOSetToProtoUTXOSet(iter)
|
||||
if err != nil {
|
||||
|
@ -159,7 +159,7 @@ func TestReindexIntervalsEarlierThanReindexRoot(t *testing.T) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, tearDown, err := factory.NewTestConsensus(params, "TestUpdateReindexRoot")
|
||||
if err != nil {
|
||||
t.Fatalf("NewTestConsensus: %s", err)
|
||||
t.Fatalf("NewTestConsensus: %+v", err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
|
@ -99,26 +99,26 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
|
||||
}
|
||||
|
||||
func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
headerTipsPruningPoint, err := sm.consensusStateManager.HeaderTipsPruningPoint()
|
||||
pruningPoint, err := sm.pruningStore.PruningPoint(sm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectedChildIterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, headerTipsPruningPoint)
|
||||
selectedChildIterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, pruningPoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lowHash := headerTipsPruningPoint
|
||||
lowHash := pruningPoint
|
||||
foundHeaderOnlyBlock := false
|
||||
for selectedChildIterator.Next() {
|
||||
selectedChild := selectedChildIterator.Get()
|
||||
selectedChildStatus, err := sm.blockStatusStore.Get(sm.databaseContext, selectedChild)
|
||||
hasBlock, err := sm.blockStore.HasBlock(sm.databaseContext, selectedChild)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if selectedChildStatus == externalapi.StatusHeaderOnly {
|
||||
if !hasBlock {
|
||||
foundHeaderOnlyBlock = true
|
||||
break
|
||||
}
|
||||
@ -167,24 +167,3 @@ func (sm *syncManager) isHeaderOnlyBlock(blockHash *externalapi.DomainHash) (boo
|
||||
|
||||
return status == externalapi.StatusHeaderOnly, nil
|
||||
}
|
||||
|
||||
func (sm *syncManager) isBlockInHeaderPruningPointFuture(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
if *blockHash == *sm.genesisBlockHash {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
exists, err := sm.blockStatusStore.Exists(sm.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !exists {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
headerTipsPruningPoint, err := sm.consensusStateManager.HeaderTipsPruningPoint()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return sm.dagTopologyManager.IsAncestorOf(headerTipsPruningPoint, blockHash)
|
||||
}
|
||||
|
@ -4,84 +4,43 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// areHeaderTipsSyncedMaxTimeDifference is the number of blocks from
|
||||
// the header virtual selected parent (estimated by timestamps) for
|
||||
// kaspad to be considered not synced
|
||||
const areHeaderTipsSyncedMaxTimeDifference = 300 // 5 minutes
|
||||
|
||||
func (sm *syncManager) syncInfo() (*externalapi.SyncInfo, error) {
|
||||
syncState, err := sm.resolveSyncState()
|
||||
isAwaitingUTXOSet, ibdRootUTXOBlockHash, err := sm.isAwaitingUTXOSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ibdRootUTXOBlockHash *externalapi.DomainHash
|
||||
if syncState == externalapi.SyncStateAwaitingUTXOSet {
|
||||
ibdRootUTXOBlockHash, err = sm.consensusStateManager.HeaderTipsPruningPoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
headerCount := sm.getHeaderCount()
|
||||
blockCount := sm.getBlockCount()
|
||||
|
||||
return &externalapi.SyncInfo{
|
||||
State: syncState,
|
||||
IsAwaitingUTXOSet: isAwaitingUTXOSet,
|
||||
IBDRootUTXOBlockHash: ibdRootUTXOBlockHash,
|
||||
HeaderCount: headerCount,
|
||||
BlockCount: blockCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sm *syncManager) resolveSyncState() (externalapi.SyncState, error) {
|
||||
hasTips, err := sm.headerTipsStore.HasTips(sm.databaseContext)
|
||||
func (sm *syncManager) isAwaitingUTXOSet() (isAwaitingUTXOSet bool, ibdRootUTXOBlockHash *externalapi.DomainHash,
|
||||
err error) {
|
||||
|
||||
pruningPointByHeaders, err := sm.pruningManager.CalculatePruningPointByHeaderSelectedTip()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !hasTips {
|
||||
return externalapi.SyncStateAwaitingGenesis, nil
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
headerVirtualSelectedParentHash, err := sm.headerVirtualSelectedParentHash()
|
||||
pruningPoint, err := sm.pruningStore.PruningPoint(sm.databaseContext)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
headerVirtualSelectedParentStatus, err := sm.blockStatusStore.Get(sm.databaseContext, headerVirtualSelectedParentHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if headerVirtualSelectedParentStatus != externalapi.StatusHeaderOnly {
|
||||
return externalapi.SyncStateSynced, nil
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
// Once the header tips are synced, check the status of
|
||||
// the pruning point from the point of view of the header
|
||||
// tips. We check it against StatusValid (rather than
|
||||
// StatusHeaderOnly) because once we do receive the
|
||||
// UTXO set of said pruning point, the state is explicitly
|
||||
// set to StatusValid.
|
||||
headerTipsPruningPoint, err := sm.consensusStateManager.HeaderTipsPruningPoint()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
headerTipsPruningPointStatus, err := sm.blockStatusStore.Get(sm.databaseContext, headerTipsPruningPoint)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if headerTipsPruningPointStatus != externalapi.StatusValid {
|
||||
return externalapi.SyncStateAwaitingUTXOSet, nil
|
||||
// If the pruning point by headers is different from the current point
|
||||
// it means we need to request the new pruning point UTXO set.
|
||||
if *pruningPoint != *pruningPointByHeaders {
|
||||
return true, pruningPointByHeaders, nil
|
||||
}
|
||||
|
||||
return externalapi.SyncStateAwaitingBlockBodies, nil
|
||||
}
|
||||
|
||||
func (sm *syncManager) headerVirtualSelectedParentHash() (*externalapi.DomainHash, error) {
|
||||
headerTips, err := sm.headerTipsStore.Tips(sm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sm.ghostdagManager.ChooseSelectedParent(headerTips...)
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
func (sm *syncManager) getHeaderCount() uint64 {
|
||||
|
@ -7,53 +7,50 @@ import (
|
||||
)
|
||||
|
||||
type syncManager struct {
|
||||
databaseContext model.DBReader
|
||||
genesisBlockHash *externalapi.DomainHash
|
||||
targetTimePerBlock int64
|
||||
databaseContext model.DBReader
|
||||
genesisBlockHash *externalapi.DomainHash
|
||||
|
||||
dagTraversalManager model.DAGTraversalManager
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
consensusStateManager model.ConsensusStateManager
|
||||
dagTraversalManager model.DAGTraversalManager
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
pruningManager model.PruningManager
|
||||
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
headerTipsStore model.HeaderTipsStore
|
||||
blockStore model.BlockStore
|
||||
pruningStore model.PruningStore
|
||||
}
|
||||
|
||||
// New instantiates a new SyncManager
|
||||
func New(
|
||||
databaseContext model.DBReader,
|
||||
genesisBlockHash *externalapi.DomainHash,
|
||||
targetTimePerBlock int64,
|
||||
dagTraversalManager model.DAGTraversalManager,
|
||||
dagTopologyManager model.DAGTopologyManager,
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
consensusStateManager model.ConsensusStateManager,
|
||||
pruningManager model.PruningManager,
|
||||
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
blockStatusStore model.BlockStatusStore,
|
||||
blockHeaderStore model.BlockHeaderStore,
|
||||
headerTipsStore model.HeaderTipsStore,
|
||||
blockStore model.BlockStore) model.SyncManager {
|
||||
blockStore model.BlockStore,
|
||||
pruningStore model.PruningStore) model.SyncManager {
|
||||
|
||||
return &syncManager{
|
||||
databaseContext: databaseContext,
|
||||
genesisBlockHash: genesisBlockHash,
|
||||
targetTimePerBlock: targetTimePerBlock,
|
||||
databaseContext: databaseContext,
|
||||
genesisBlockHash: genesisBlockHash,
|
||||
|
||||
dagTraversalManager: dagTraversalManager,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
ghostdagManager: ghostdagManager,
|
||||
consensusStateManager: consensusStateManager,
|
||||
dagTraversalManager: dagTraversalManager,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
ghostdagManager: ghostdagManager,
|
||||
pruningManager: pruningManager,
|
||||
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
headerTipsStore: headerTipsStore,
|
||||
blockStore: blockStore,
|
||||
pruningStore: pruningStore,
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,13 +68,6 @@ func (sm *syncManager) GetMissingBlockBodyHashes(highHash *externalapi.DomainHas
|
||||
return sm.missingBlockBodyHashes(highHash)
|
||||
}
|
||||
|
||||
func (sm *syncManager) IsBlockInHeaderPruningPointFuture(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "IsBlockInHeaderPruningPointFuture")
|
||||
defer onEnd()
|
||||
|
||||
return sm.isBlockInHeaderPruningPointFuture(blockHash)
|
||||
}
|
||||
|
||||
func (sm *syncManager) CreateBlockLocator(lowHash, highHash *externalapi.DomainHash, limit uint32) (externalapi.BlockLocator, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "CreateBlockLocator")
|
||||
defer onEnd()
|
||||
|
@ -237,6 +237,8 @@ var (
|
||||
|
||||
//ErrBlockIsTooMuchInTheFuture indicates that the block timestamp is too much in the future.
|
||||
ErrBlockIsTooMuchInTheFuture = newRuleError("ErrBlockIsTooMuchInTheFuture")
|
||||
|
||||
ErrUnexpectedPruningPoint = newRuleError("ErrUnexpectedPruningPoint")
|
||||
)
|
||||
|
||||
// RuleError identifies a rule violation. It is used to indicate that
|
||||
|
@ -37,8 +37,8 @@ func (tc *testConsensus) GHOSTDAGDataStore() model.GHOSTDAGDataStore {
|
||||
return tc.ghostdagDataStore
|
||||
}
|
||||
|
||||
func (tc *testConsensus) HeaderTipsStore() model.HeaderTipsStore {
|
||||
return tc.headerTipsStore
|
||||
func (tc *testConsensus) HeaderTipsStore() model.HeaderSelectedTipStore {
|
||||
return tc.headersSelectedTipStore
|
||||
}
|
||||
|
||||
func (tc *testConsensus) MultisetStore() model.MultisetStore {
|
||||
@ -93,7 +93,7 @@ func (tc *testConsensus) GHOSTDAGManager() model.GHOSTDAGManager {
|
||||
return tc.ghostdagManager
|
||||
}
|
||||
|
||||
func (tc *testConsensus) HeaderTipsManager() model.HeaderTipsManager {
|
||||
func (tc *testConsensus) HeaderTipsManager() model.HeadersSelectedTipManager {
|
||||
return tc.headerTipsManager
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,11 @@ func (networkFlags *NetworkFlags) ResolveNetwork(parser *flags.Parser) error {
|
||||
return errors.Errorf("Mainnet has not launched yet, use --testnet to run in testnet mode")
|
||||
}
|
||||
|
||||
err := networkFlags.overrideDAGParams()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,8 @@ var (
|
||||
snvrLog = BackendLog.Logger("SNVR")
|
||||
wsvcLog = BackendLog.Logger("WSVC")
|
||||
reacLog = BackendLog.Logger("REAC")
|
||||
prnmLog = BackendLog.Logger("PRNM")
|
||||
blvlLog = BackendLog.Logger("BLVL")
|
||||
)
|
||||
|
||||
// SubsystemTags is an enum of all sub system tags
|
||||
@ -85,7 +87,9 @@ var SubsystemTags = struct {
|
||||
DNSS,
|
||||
SNVR,
|
||||
WSVC,
|
||||
REAC string
|
||||
REAC,
|
||||
PRNM,
|
||||
BLVL string
|
||||
}{
|
||||
ADXR: "ADXR",
|
||||
AMGR: "AMGR",
|
||||
@ -115,6 +119,8 @@ var SubsystemTags = struct {
|
||||
SNVR: "SNVR",
|
||||
WSVC: "WSVC",
|
||||
REAC: "REAC",
|
||||
PRNM: "PRNM",
|
||||
BLVL: "BLVL",
|
||||
}
|
||||
|
||||
// subsystemLoggers maps each subsystem identifier to its associated logger.
|
||||
@ -147,6 +153,8 @@ var subsystemLoggers = map[string]*Logger{
|
||||
SubsystemTags.SNVR: snvrLog,
|
||||
SubsystemTags.WSVC: wsvcLog,
|
||||
SubsystemTags.REAC: reacLog,
|
||||
SubsystemTags.PRNM: prnmLog,
|
||||
SubsystemTags.BLVL: blvlLog,
|
||||
}
|
||||
|
||||
// InitLog attaches log file and error log file to the backend log.
|
||||
|
Loading…
x
Reference in New Issue
Block a user