[NOD-1511] Implement missingBlockBodyHashes (#1000)

* [NOD-1511] Implement missingBlockBodyHashes

* [NOD-1511] Rename selectedparentiterator.go to blockiterator.go

* [NOD-1511] Fix condition

* [NOD-1511] Simplify missingBlocks logic
This commit is contained in:
Ori Newman 2020-11-05 00:11:42 -08:00 committed by GitHub
parent baf8d25656
commit 52c73d3a08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 211 additions and 14 deletions

View File

@ -183,6 +183,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
genesisHash,
dagTraversalManager,
dagTopologyManager,
consensusStateManager,
ghostdagDataStore,
blockStatusStore)

View File

@ -2,9 +2,8 @@ package model
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// SelectedParentIterator is an iterator over the selected parent
// chain of some block
type SelectedParentIterator interface {
// BlockIterator is an iterator over blocks according to some order.
type BlockIterator interface {
Next() bool
Get() *externalapi.DomainHash
}

View File

@ -8,4 +8,5 @@ type ConsensusStateManager interface {
PopulateTransactionWithUTXOEntries(transaction *externalapi.DomainTransaction) error
SetPruningPointUTXOSet(serializedUTXOSet []byte) error
RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (ReadOnlyUTXOSetIterator, error)
HeaderTipsPruningPoint() (*externalapi.DomainHash, error)
}

View File

@ -7,7 +7,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type DAGTraversalManager interface {
HighestChainBlockBelowBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
LowestChainBlockAboveOrEqualToBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
SelectedParentIterator(highHash *externalapi.DomainHash) SelectedParentIterator
SelectedParentIterator(highHash *externalapi.DomainHash) BlockIterator
SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
AnticoneFromContext(context, lowHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
BlueWindow(highHash *externalapi.DomainHash, windowSize uint64) ([]*externalapi.DomainHash, error)
NewDownHeap() BlockHeap
NewUpHeap() BlockHeap

View File

@ -29,7 +29,7 @@ func (csm *consensusStateManager) SetPruningPointUTXOSet(serializedUTXOSet []byt
}
func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byte) error {
headerTipsPruningPoint, err := csm.headerTipsPruningPoint()
headerTipsPruningPoint, err := csm.HeaderTipsPruningPoint()
if err != nil {
return err
}
@ -98,7 +98,7 @@ func deserializeUTXOSet(serializedUTXOSet []byte) model.ReadOnlyUTXOSetIterator
panic("implement me")
}
func (csm *consensusStateManager) headerTipsPruningPoint() (*externalapi.DomainHash, error) {
func (csm *consensusStateManager) HeaderTipsPruningPoint() (*externalapi.DomainHash, error) {
headerTips, err := csm.headerTipsStore.Tips(csm.databaseContext)
if err != nil {
return nil, err

View File

@ -0,0 +1,53 @@
package dagtraversalmanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
)
// AnticoneFromContext returns blocks in (context.Past ⋂ block.Anticone)
func (dtm *dagTraversalManager) AnticoneFromContext(context, block *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
anticone := []*externalapi.DomainHash{}
queue := []*externalapi.DomainHash{context}
visited := hashset.New()
for len(queue) > 0 {
var current *externalapi.DomainHash
current, queue = queue[0], queue[1:]
if visited.Contains(current) {
continue
}
visited.Add(current)
currentIsAncestorOfBlock, err := dtm.dagTopologyManager.IsAncestorOf(current, block)
if err != nil {
return nil, err
}
if currentIsAncestorOfBlock {
continue
}
blockIsAncestorOfCurrent, err := dtm.dagTopologyManager.IsAncestorOf(block, current)
if err != nil {
return nil, err
}
if !blockIsAncestorOfCurrent {
anticone = append(anticone, current)
}
currentParents, err := dtm.dagTopologyManager.Parents(current)
if err != nil {
return nil, err
}
for _, parent := range currentParents {
queue = append(queue, parent)
}
}
return anticone, nil
}

View File

@ -16,7 +16,7 @@ type dagTraversalManager struct {
ghostdagManager model.GHOSTDAGManager
}
// selectedParentIterator implements the `model.SelectedParentIterator` API
// selectedParentIterator implements the `model.BlockIterator` API
type selectedParentIterator struct {
databaseContext model.DBReader
ghostdagDataStore model.GHOSTDAGDataStore
@ -55,7 +55,7 @@ func New(
// SelectedParentIterator creates an iterator over the selected
// parent chain of the given highHash
func (dtm *dagTraversalManager) SelectedParentIterator(highHash *externalapi.DomainHash) model.SelectedParentIterator {
func (dtm *dagTraversalManager) SelectedParentIterator(highHash *externalapi.DomainHash) model.BlockIterator {
return &selectedParentIterator{
databaseContext: dtm.databaseContext,
ghostdagDataStore: dtm.ghostdagDataStore,
@ -79,7 +79,7 @@ func (dtm *dagTraversalManager) HighestChainBlockBelowBlueScore(highHash *extern
requiredBlueScore := chainBlock.BlueScore - blueScore
// If we used `SelectedParentIterator` we'd need to do more calls to `ghostdagDataStore` so we can get the blueScore
// If we used `BlockIterator` we'd need to do more calls to `ghostdagDataStore` so we can get the blueScore
for chainBlock.BlueScore >= requiredBlueScore {
if chainBlock.SelectedParent == nil { // genesis
return blockHash, nil

View File

@ -0,0 +1,55 @@
package dagtraversalmanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/pkg/errors"
)
type selectedChildIterator struct {
databaseContext model.DBReader
dagTopologyManager model.DAGTopologyManager
highHash *externalapi.DomainHash
current *externalapi.DomainHash
}
func (s selectedChildIterator) Next() bool {
children, err := s.dagTopologyManager.Children(s.current)
if err != nil {
panic(err)
}
for _, child := range children {
isChildInSelectedParentChainOfHighHash, err := s.dagTopologyManager.IsInSelectedParentChainOf(child, s.highHash)
if err != nil {
panic(err)
}
if isChildInSelectedParentChainOfHighHash {
s.current = child
return true
}
}
return false
}
func (s selectedChildIterator) Get() *externalapi.DomainHash {
return s.current
}
func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (model.BlockIterator, error) {
isLowHashInSelectedParentChainOfHighHash, err := dtm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash)
if err != nil {
return nil, err
}
if !isLowHashInSelectedParentChainOfHighHash {
return nil, errors.Errorf("%s is not in the selected parent chain of %s", highHash, lowHash)
}
return &selectedChildIterator{
databaseContext: dtm.databaseContext,
dagTopologyManager: dtm.dagTopologyManager,
highHash: highHash,
current: lowHash,
}, nil
}

View File

@ -99,7 +99,83 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
}
func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
panic("implement me")
headerTipsPruningPoint, err := sm.consensusStateManager.HeaderTipsPruningPoint()
if err != nil {
return nil, err
}
selectedChildIterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, headerTipsPruningPoint)
if err != nil {
return nil, err
}
lowHash := headerTipsPruningPoint
for selectedChildIterator.Next() {
lowHash = selectedChildIterator.Get()
}
hashesBetween, err := sm.antiPastHashesBetween(lowHash, highHash)
if err != nil {
return nil, err
}
lowHashAnticone, err := sm.dagTraversalManager.AnticoneFromContext(highHash, lowHash)
if err != nil {
return nil, err
}
blockToRemoveFromHashesBetween := hashset.New()
for _, blockHash := range lowHashAnticone {
isHeaderOnlyBlock, err := sm.isHeaderOnlyBlock(blockHash)
if err != nil {
return nil, err
}
if !isHeaderOnlyBlock {
blockToRemoveFromHashesBetween.Add(blockHash)
}
}
missingBlocks := make([]*externalapi.DomainHash, 0, len(hashesBetween)-len(lowHashAnticone))
for i, blockHash := range hashesBetween {
// If blockToRemoveFromHashesBetween is empty, no more blocks should be
// filtered, so we can copy the rest of hashesBetween into missingBlocks
if blockToRemoveFromHashesBetween.Length() == 0 {
missingBlocks = append(missingBlocks, hashesBetween[i:]...)
break
}
if blockToRemoveFromHashesBetween.Contains(blockHash) {
blockToRemoveFromHashesBetween.Remove(blockHash)
continue
}
missingBlocks = append(missingBlocks, blockHash)
}
if blockToRemoveFromHashesBetween.Length() != 0 {
return nil, errors.Errorf("blockToRemoveFromHashesBetween.Length() is expected to be 0")
}
return missingBlocks, nil
}
func (sm *syncManager) isHeaderOnlyBlock(blockHash *externalapi.DomainHash) (bool, error) {
exists, err := sm.blockStatusStore.Exists(sm.databaseContext, blockHash)
if err != nil {
return false, err
}
if !exists {
return false, nil
}
status, err := sm.blockStatusStore.Get(sm.databaseContext, blockHash)
if err != nil {
return false, err
}
return status == externalapi.StatusHeaderOnly, nil
}
func (sm *syncManager) isBlockInHeaderPruningPointFutureAndVirtualPast(blockHash *externalapi.DomainHash) (bool, error) {

View File

@ -10,8 +10,9 @@ type syncManager struct {
databaseContext model.DBReader
genesisBlockHash *externalapi.DomainHash
dagTraversalManager model.DAGTraversalManager
dagTopologyManager model.DAGTopologyManager
dagTraversalManager model.DAGTraversalManager
dagTopologyManager model.DAGTopologyManager
consensusStateManager model.ConsensusStateManager
ghostdagDataStore model.GHOSTDAGDataStore
blockStatusStore model.BlockStatusStore
@ -23,6 +24,8 @@ func New(
genesisBlockHash *externalapi.DomainHash,
dagTraversalManager model.DAGTraversalManager,
dagTopologyManager model.DAGTopologyManager,
consensusStateManager model.ConsensusStateManager,
ghostdagDataStore model.GHOSTDAGDataStore,
blockStatusStore model.BlockStatusStore) model.SyncManager {
@ -30,8 +33,9 @@ func New(
databaseContext: databaseContext,
genesisBlockHash: genesisBlockHash,
dagTraversalManager: dagTraversalManager,
dagTopologyManager: dagTopologyManager,
dagTraversalManager: dagTraversalManager,
dagTopologyManager: dagTopologyManager,
consensusStateManager: consensusStateManager,
ghostdagDataStore: ghostdagDataStore,
blockStatusStore: blockStatusStore,

View File

@ -84,3 +84,8 @@ func (hs HashSet) ToSlice() []*externalapi.DomainHash {
return slice
}
// Length returns the length of this HashSet
func (hs HashSet) Length() int {
return hs.Length()
}