Prune blocks below pruning point when moving pruning point during IBD (#1513)

* Split deletePastBlocks into sub-routines

* Remove SelectedParentIterator, and refactor SelectedChildIterator to support First and Error

* Implement PruneAllBlocks

* Prune all blocks in the store

* Prune only blocks that are below the pruning point

* Defer call onEnd of LogAndMeasureExecutionTime

* Handle a forgotten error

* Minor style fixes
This commit is contained in:
Svarog 2021-02-10 16:39:36 +02:00 committed by GitHub
parent f13fc35b9e
commit 1222a555f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 186 additions and 96 deletions

View File

@ -212,3 +212,34 @@ func (bs *blockStore) serializeBlockCount(count uint64) ([]byte, error) {
dbBlockCount := &serialization.DbBlockCount{Count: count} dbBlockCount := &serialization.DbBlockCount{Count: count}
return proto.Marshal(dbBlockCount) return proto.Marshal(dbBlockCount)
} }
type allBlockHashesIterator struct {
cursor model.DBCursor
}
func (a allBlockHashesIterator) First() bool {
return a.cursor.First()
}
func (a allBlockHashesIterator) Next() bool {
return a.cursor.Next()
}
func (a allBlockHashesIterator) Get() (*externalapi.DomainHash, error) {
key, err := a.cursor.Key()
if err != nil {
return nil, err
}
blockHashBytes := key.Suffix()
return externalapi.NewDomainHashFromByteSlice(blockHashBytes)
}
func (bs *blockStore) AllBlockHashesIterator(dbContext model.DBReader) (model.BlockIterator, error) {
cursor, err := dbContext.Cursor(bucket)
if err != nil {
return nil, err
}
return &allBlockHashesIterator{cursor: cursor}, nil
}

View File

@ -5,6 +5,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// BlockHeap represents a heap of block hashes, providing a priority-queue functionality // BlockHeap represents a heap of block hashes, providing a priority-queue functionality
type BlockHeap interface { type BlockHeap interface {
Push(blockHash *externalapi.DomainHash) error Push(blockHash *externalapi.DomainHash) error
PushSlice(blockHash []*externalapi.DomainHash) error
Pop() *externalapi.DomainHash Pop() *externalapi.DomainHash
Len() int Len() int
ToSlice() []*externalapi.DomainHash ToSlice() []*externalapi.DomainHash

View File

@ -4,6 +4,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// BlockIterator is an iterator over blocks according to some order. // BlockIterator is an iterator over blocks according to some order.
type BlockIterator interface { type BlockIterator interface {
First() bool
Next() bool Next() bool
Get() *externalapi.DomainHash Get() (*externalapi.DomainHash, error)
} }

View File

@ -12,4 +12,5 @@ type BlockStore interface {
Blocks(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error) Blocks(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error)
Delete(blockHash *externalapi.DomainHash) Delete(blockHash *externalapi.DomainHash)
Count() uint64 Count() uint64
AllBlockHashesIterator(dbContext DBReader) (BlockIterator, error)
} }

View File

@ -10,7 +10,6 @@ type DAGTopologyManager interface {
IsParentOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) IsParentOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
IsChildOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) IsChildOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
IsAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) IsAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error)
IsInSelectedParentChainOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) IsInSelectedParentChainOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
ChildInSelectedParentChainOf(context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) ChildInSelectedParentChainOf(context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error)

View File

@ -7,7 +7,8 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type DAGTraversalManager interface { type DAGTraversalManager interface {
BlockAtDepth(highHash *externalapi.DomainHash, depth uint64) (*externalapi.DomainHash, error) BlockAtDepth(highHash *externalapi.DomainHash, depth uint64) (*externalapi.DomainHash, error)
LowestChainBlockAboveOrEqualToBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error) LowestChainBlockAboveOrEqualToBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
SelectedParentIterator(highHash *externalapi.DomainHash) BlockIterator // SelectedChildIterator should return a BlockIterator that iterates
// from lowHash (exclusive) to highHash (inclusive) over highHash's selected parent chain
SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error) SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
AnticoneFromContext(context, lowHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) AnticoneFromContext(context, lowHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
BlueWindow(highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error) BlueWindow(highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error)

View File

@ -9,4 +9,5 @@ type PruningManager interface {
ClearImportedPruningPointData() error ClearImportedPruningPointData() error
AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) error AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) error
UpdatePruningPointUTXOSetIfRequired() error UpdatePruningPointUTXOSetIfRequired() error
PruneAllBlocksBelow(pruningPointHash *externalapi.DomainHash) error
} }

View File

@ -28,6 +28,12 @@ func (bp *blockProcessor) validateAndInsertImportedPruningPoint(newPruningPoint
return err return err
} }
log.Info("Deleting block data for all blocks in blockStore")
err = bp.pruningManager.PruneAllBlocksBelow(newPruningPointHash)
if err != nil {
return err
}
log.Infof("Updating consensus state manager according to the new pruning point %s", newPruningPointHash) log.Infof("Updating consensus state manager according to the new pruning point %s", newPruningPointHash)
err = bp.consensusStateManager.ImportPruningPoint(newPruningPoint) err = bp.consensusStateManager.ImportPruningPoint(newPruningPoint)
if err != nil { if err != nil {

View File

@ -61,7 +61,7 @@ func (csm *consensusStateManager) importPruningPoint(newPruningPoint *externalap
} }
log.Debugf("The new pruning point UTXO commitment validation passed") log.Debugf("The new pruning point UTXO commitment validation passed")
log.Debugf("Staging the the pruning point as the only DAG tip") log.Debugf("Staging the pruning point as the only DAG tip")
newTips := []*externalapi.DomainHash{newPruningPointHash} newTips := []*externalapi.DomainHash{newPruningPointHash}
csm.consensusStateStore.StageTips(newTips) csm.consensusStateStore.StageTips(newTips)

View File

@ -71,11 +71,6 @@ func (dtm *dagTopologyManager) IsAncestorOf(blockHashA *externalapi.DomainHash,
return dtm.reachabilityManager.IsDAGAncestorOf(blockHashA, blockHashB) return dtm.reachabilityManager.IsDAGAncestorOf(blockHashA, blockHashB)
} }
// IsDescendantOf returns true if blockHashA is a DAG descendant of blockHashB
func (dtm *dagTopologyManager) IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) {
return dtm.reachabilityManager.IsDAGAncestorOf(blockHashB, blockHashA)
}
// IsAncestorOfAny returns true if `blockHash` is an ancestor of at least one of `potentialDescendants` // IsAncestorOfAny returns true if `blockHash` is an ancestor of at least one of `potentialDescendants`
func (dtm *dagTopologyManager) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) { func (dtm *dagTopologyManager) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) {
for _, potentialDescendant := range potentialDescendants { for _, potentialDescendant := range potentialDescendants {

View File

@ -109,6 +109,16 @@ func (bh *blockHeap) Push(blockHash *externalapi.DomainHash) error {
return nil return nil
} }
func (bh *blockHeap) PushSlice(blockHashes []*externalapi.DomainHash) error {
for _, blockHash := range blockHashes {
err := bh.Push(blockHash)
if err != nil {
return err
}
}
return nil
}
// Len returns the length of this heap // Len returns the length of this heap
func (bh *blockHeap) Len() int { func (bh *blockHeap) Len() int {
return bh.impl.Len() return bh.impl.Len()

View File

@ -1,7 +1,6 @@
package dagtraversalmanager package dagtraversalmanager
import ( import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -18,29 +17,6 @@ type dagTraversalManager struct {
reachabilityDataStore model.ReachabilityDataStore reachabilityDataStore model.ReachabilityDataStore
} }
// selectedParentIterator implements the `model.BlockIterator` API
type selectedParentIterator struct {
databaseContext model.DBReader
ghostdagDataStore model.GHOSTDAGDataStore
current *externalapi.DomainHash
}
func (spi *selectedParentIterator) Next() bool {
if spi.current == nil {
return false
}
ghostdagData, err := spi.ghostdagDataStore.Get(spi.databaseContext, spi.current)
if err != nil {
panic(fmt.Sprintf("ghostdagDataStore is missing ghostdagData for: %v. '%s' ", spi.current, err))
}
spi.current = ghostdagData.SelectedParent()
return spi.current != nil
}
func (spi *selectedParentIterator) Get() *externalapi.DomainHash {
return spi.current
}
// New instantiates a new DAGTraversalManager // New instantiates a new DAGTraversalManager
func New( func New(
databaseContext model.DBReader, databaseContext model.DBReader,
@ -57,16 +33,6 @@ func New(
} }
} }
// SelectedParentIterator creates an iterator over the selected
// parent chain of the given highHash
func (dtm *dagTraversalManager) SelectedParentIterator(highHash *externalapi.DomainHash) model.BlockIterator {
return &selectedParentIterator{
databaseContext: dtm.databaseContext,
ghostdagDataStore: dtm.ghostdagDataStore,
current: highHash,
}
}
// BlockAtDepth returns the hash of the highest block with a blue score // BlockAtDepth returns the hash of the highest block with a blue score
// lower than (highHash.blueSore - depth) in the selected-parent-chain // lower than (highHash.blueSore - depth) in the selected-parent-chain
// of the block with the given highHash's selected parent chain. // of the block with the given highHash's selected parent chain.

View File

@ -11,20 +11,34 @@ type selectedChildIterator struct {
dagTopologyManager model.DAGTopologyManager dagTopologyManager model.DAGTopologyManager
reachabilityDataStore model.ReachabilityDataStore reachabilityDataStore model.ReachabilityDataStore
highHash *externalapi.DomainHash highHash, lowHash *externalapi.DomainHash
current *externalapi.DomainHash current *externalapi.DomainHash
err error
}
func (s *selectedChildIterator) First() bool {
s.current = s.lowHash
return s.Next()
} }
func (s *selectedChildIterator) Next() bool { func (s *selectedChildIterator) Next() bool {
if s.err != nil {
return true
}
data, err := s.reachabilityDataStore.ReachabilityData(s.databaseContext, s.current) data, err := s.reachabilityDataStore.ReachabilityData(s.databaseContext, s.current)
if err != nil { if err != nil {
panic(err) s.current = nil
s.err = err
return true
} }
for _, child := range data.Children() { for _, child := range data.Children() {
isChildInSelectedParentChainOfHighHash, err := s.dagTopologyManager.IsInSelectedParentChainOf(child, s.highHash) isChildInSelectedParentChainOfHighHash, err := s.dagTopologyManager.IsInSelectedParentChainOf(child, s.highHash)
if err != nil { if err != nil {
panic(err) s.current = nil
s.err = err
return true
} }
if isChildInSelectedParentChainOfHighHash { if isChildInSelectedParentChainOfHighHash {
@ -35,10 +49,12 @@ func (s *selectedChildIterator) Next() bool {
return false return false
} }
func (s *selectedChildIterator) Get() *externalapi.DomainHash { func (s *selectedChildIterator) Get() (*externalapi.DomainHash, error) {
return s.current return s.current, s.err
} }
// SelectedChildIterator returns a BlockIterator that iterates from lowHash (exclusive) to highHash (inclusive) over
// highHash's selected parent chain
func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (model.BlockIterator, error) { func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (model.BlockIterator, error) {
isLowHashInSelectedParentChainOfHighHash, err := dtm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash) isLowHashInSelectedParentChainOfHighHash, err := dtm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash)
if err != nil { if err != nil {
@ -53,6 +69,7 @@ func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externa
dagTopologyManager: dtm.dagTopologyManager, dagTopologyManager: dtm.dagTopologyManager,
reachabilityDataStore: dtm.reachabilityDataStore, reachabilityDataStore: dtm.reachabilityDataStore,
highHash: highHash, highHash: highHash,
lowHash: lowHash,
current: lowHash, current: lowHash,
}, nil }, nil
} }

View File

@ -2,9 +2,6 @@ package ghostdagmanager_test
import ( import (
"encoding/json" "encoding/json"
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/util/difficulty"
"math" "math"
"math/big" "math/big"
"os" "os"
@ -12,6 +9,10 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/util/difficulty"
"github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2" "github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2"
@ -386,10 +387,6 @@ func (dt *DAGTopologyManagerImpl) IsAncestorOf(hashBlockA *externalapi.DomainHas
} }
func (dt *DAGTopologyManagerImpl) IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) {
panic("unimplemented")
}
func (dt *DAGTopologyManagerImpl) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) { func (dt *DAGTopologyManagerImpl) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) {
panic("unimplemented") panic("unimplemented")
} }

View File

@ -151,8 +151,11 @@ func (pm *pruningManager) UpdatePruningPointByVirtual() error {
newPruningPoint := currentPruningPoint newPruningPoint := currentPruningPoint
newPruningPointGHOSTDAGData := currentPruningPointGHOSTDAGData newPruningPointGHOSTDAGData := currentPruningPointGHOSTDAGData
for iterator.Next() { for ok := iterator.First(); ok; ok = iterator.Next() {
selectedChild := iterator.Get() selectedChild, err := iterator.Get()
if err != nil {
return err
}
selectedChildGHOSTDAGData, err := pm.ghostdagDataStore.Get(pm.databaseContext, selectedChild) selectedChildGHOSTDAGData, err := pm.ghostdagDataStore.Get(pm.databaseContext, selectedChild)
if err != nil { if err != nil {
return err return err
@ -221,47 +224,47 @@ func (pm *pruningManager) deletePastBlocks(pruningPoint *externalapi.DomainHash)
onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.deletePastBlocks") onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.deletePastBlocks")
defer onEnd() defer onEnd()
// Go over all P.Past and P.AC that's not in V.Past // Go over all pruningPoint.Past and pruningPoint.Anticone that's not in virtual.Past
queue := pm.dagTraversalManager.NewDownHeap() queue := pm.dagTraversalManager.NewDownHeap()
// Find P.AC that's not in V.Past
dagTips, err := pm.consensusStateStore.Tips(pm.databaseContext)
if err != nil {
return err
}
newTips := make([]*externalapi.DomainHash, 0, len(dagTips))
virtualParents, err := pm.dagTopologyManager.Parents(model.VirtualBlockHash) virtualParents, err := pm.dagTopologyManager.Parents(model.VirtualBlockHash)
if err != nil { if err != nil {
return err return err
} }
for _, tip := range dagTips {
isInPruningFutureOrInVirtualPast, err := pm.isInPruningFutureOrInVirtualPast(tip, pruningPoint, virtualParents) // Start queue with all tips that are below the pruning point (and on the way remove them from list of tips)
if err != nil { prunedTips, err := pm.pruneTips(pruningPoint, virtualParents)
return err if err != nil {
} return err
if !isInPruningFutureOrInVirtualPast {
// Add them to the queue so they and their past will be pruned
err := queue.Push(tip)
if err != nil {
return err
}
} else {
newTips = append(newTips, tip)
}
} }
pm.consensusStateStore.StageTips(newTips) err = queue.PushSlice(prunedTips)
// Add P.Parents if err != nil {
return err
}
// Add pruningPoint.Parents to queue
parents, err := pm.dagTopologyManager.Parents(pruningPoint) parents, err := pm.dagTopologyManager.Parents(pruningPoint)
if err != nil { if err != nil {
return err return err
} }
for _, parent := range parents { err = queue.PushSlice(parents)
err = queue.Push(parent) if err != nil {
if err != nil { return err
return err
}
} }
err = pm.deleteBlocksDownward(queue)
if err != nil {
return err
}
err = pm.pruneVirtualDiffParents(pruningPoint, virtualParents)
if err != nil {
return err
}
return nil
}
func (pm *pruningManager) deleteBlocksDownward(queue model.BlockHeap) error {
visited := map[externalapi.DomainHash]struct{}{} visited := map[externalapi.DomainHash]struct{}{}
// Prune everything in the queue including its past // Prune everything in the queue including its past
for queue.Len() > 0 { for queue.Len() > 0 {
@ -280,16 +283,16 @@ func (pm *pruningManager) deletePastBlocks(pruningPoint *externalapi.DomainHash)
if err != nil { if err != nil {
return err return err
} }
for _, parent := range parents { err = queue.PushSlice(parents)
err = queue.Push(parent) if err != nil {
if err != nil { return err
return err
}
} }
} }
} }
return nil
}
// Delete virtual diff parents that are in PruningPoint's Anticone and not in Virtual's Past func (pm *pruningManager) pruneVirtualDiffParents(pruningPoint *externalapi.DomainHash, virtualParents []*externalapi.DomainHash) error {
virtualDiffParents, err := pm.consensusStateStore.VirtualDiffParents(pm.databaseContext) virtualDiffParents, err := pm.consensusStateStore.VirtualDiffParents(pm.databaseContext)
if err != nil { if err != nil {
return err return err
@ -309,6 +312,31 @@ func (pm *pruningManager) deletePastBlocks(pruningPoint *externalapi.DomainHash)
return nil return nil
} }
func (pm *pruningManager) pruneTips(pruningPoint *externalapi.DomainHash, virtualParents []*externalapi.DomainHash) (
prunedTips []*externalapi.DomainHash, err error) {
// Find P.AC that's not in V.Past
dagTips, err := pm.consensusStateStore.Tips(pm.databaseContext)
if err != nil {
return nil, err
}
newTips := make([]*externalapi.DomainHash, 0, len(dagTips))
for _, tip := range dagTips {
isInPruningFutureOrInVirtualPast, err := pm.isInPruningFutureOrInVirtualPast(tip, pruningPoint, virtualParents)
if err != nil {
return nil, err
}
if !isInPruningFutureOrInVirtualPast {
prunedTips = append(prunedTips, tip)
} else {
newTips = append(newTips, tip)
}
}
pm.consensusStateStore.StageTips(newTips)
return prunedTips, nil
}
func (pm *pruningManager) savePruningPoint(pruningPointHash *externalapi.DomainHash) error { func (pm *pruningManager) savePruningPoint(pruningPointHash *externalapi.DomainHash) error {
onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.savePruningPoint") onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.savePruningPoint")
defer onEnd() defer onEnd()
@ -551,3 +579,33 @@ func (pm *pruningManager) updatePruningPointUTXOSet() error {
log.Debugf("Finishing updating the pruning point UTXO set") log.Debugf("Finishing updating the pruning point UTXO set")
return pm.pruningStore.FinishUpdatingPruningPointUTXOSet(pm.databaseContext) return pm.pruningStore.FinishUpdatingPruningPointUTXOSet(pm.databaseContext)
} }
func (pm *pruningManager) PruneAllBlocksBelow(pruningPointHash *externalapi.DomainHash) error {
onEnd := logger.LogAndMeasureExecutionTime(log, "PruneAllBlocksBelow")
defer onEnd()
iterator, err := pm.blocksStore.AllBlockHashesIterator(pm.databaseContext)
if err != nil {
return err
}
for ok := iterator.First(); ok; ok = iterator.Next() {
blockHash, err := iterator.Get()
if err != nil {
return err
}
isInPastOfPruningPoint, err := pm.dagTopologyManager.IsAncestorOf(pruningPointHash, blockHash)
if err != nil {
return err
}
if !isInPastOfPruningPoint {
continue
}
_, err = pm.deleteBlock(blockHash)
if err != nil {
return err
}
}
return nil
}

View File

@ -39,8 +39,11 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
if err != nil { if err != nil {
return nil, err return nil, err
} }
for iterator.Next() { for ok := iterator.First(); ok; ok = iterator.Next() {
highHash = iterator.Get() highHash, err = iterator.Get()
if err != nil {
return nil, err
}
highBlockGHOSTDAGData, err = sm.ghostdagDataStore.Get(sm.databaseContext, highHash) highBlockGHOSTDAGData, err = sm.ghostdagDataStore.Get(sm.databaseContext, highHash)
if err != nil { if err != nil {
return nil, err return nil, err
@ -112,8 +115,11 @@ func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash)
lowHash := pruningPoint lowHash := pruningPoint
foundHeaderOnlyBlock := false foundHeaderOnlyBlock := false
for selectedChildIterator.Next() { for ok := selectedChildIterator.First(); ok; ok = selectedChildIterator.Next() {
selectedChild := selectedChildIterator.Get() selectedChild, err := selectedChildIterator.Get()
if err != nil {
return nil, err
}
hasBlock, err := sm.blockStore.HasBlock(sm.databaseContext, selectedChild) hasBlock, err := sm.blockStore.HasBlock(sm.databaseContext, selectedChild)
if err != nil { if err != nil {
return nil, err return nil, err