171 lines
4.8 KiB
Go

package syncmanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
"github.com/pkg/errors"
)
// antiPastHashesBetween returns the hashes of the blocks between the
// lowHash's antiPast and highHash's antiPast, or up to
// `maxBlueScoreDifference`, if non-zero.
func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.DomainHash,
maxBlueScoreDifference uint64) ([]*externalapi.DomainHash, error) {
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
if err != nil {
return nil, err
}
highBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, highHash)
if err != nil {
return nil, err
}
if lowBlockGHOSTDAGData.BlueScore() >= highBlockGHOSTDAGData.BlueScore() {
return nil, errors.Errorf("low hash blueScore >= high hash blueScore (%d >= %d)",
lowBlockGHOSTDAGData.BlueScore(), highBlockGHOSTDAGData.BlueScore())
}
if maxBlueScoreDifference != 0 {
// In order to get no more then maxBlueScoreDifference
// blocks from the future of the lowHash (including itself),
// we iterate the selected parent chain of the highNode and
// stop once we reach
// highBlockBlueScore-lowBlockBlueScore+1 <= maxBlueScoreDifference.
// That stop point becomes the new highHash.
// Using blueScore as an approximation is considered to be
// fairly accurate because we presume that most DAG blocks are
// blue.
iterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, lowHash)
if err != nil {
return nil, err
}
for iterator.Next() {
highHash = iterator.Get()
highBlockGHOSTDAGData, err = sm.ghostdagDataStore.Get(sm.databaseContext, highHash)
if err != nil {
return nil, err
}
if highBlockGHOSTDAGData.BlueScore()-lowBlockGHOSTDAGData.BlueScore()+1 > maxBlueScoreDifference {
break
}
}
}
// Collect every node in highHash's past (including itself) but
// NOT in the lowHash's past (excluding itself) into an up-heap
// (a heap sorted by blueScore from lowest to greatest).
visited := hashset.New()
hashesUpHeap := sm.dagTraversalManager.NewUpHeap()
queue := sm.dagTraversalManager.NewDownHeap()
err = queue.Push(highHash)
if err != nil {
return nil, err
}
for queue.Len() > 0 {
current := queue.Pop()
if visited.Contains(current) {
continue
}
visited.Add(current)
var isCurrentAncestorOfLowHash bool
if current == lowHash {
isCurrentAncestorOfLowHash = false
} else {
var err error
isCurrentAncestorOfLowHash, err = sm.dagTopologyManager.IsAncestorOf(current, lowHash)
if err != nil {
return nil, err
}
}
if isCurrentAncestorOfLowHash {
continue
}
err = hashesUpHeap.Push(current)
if err != nil {
return nil, err
}
parents, err := sm.dagTopologyManager.Parents(current)
if err != nil {
return nil, err
}
for _, parent := range parents {
err := queue.Push(parent)
if err != nil {
return nil, err
}
}
}
return hashesUpHeap.ToSlice(), nil
}
func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
pruningPoint, err := sm.pruningStore.PruningPoint(sm.databaseContext)
if err != nil {
return nil, err
}
selectedChildIterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, pruningPoint)
if err != nil {
return nil, err
}
lowHash := pruningPoint
foundHeaderOnlyBlock := false
for selectedChildIterator.Next() {
selectedChild := selectedChildIterator.Get()
hasBlock, err := sm.blockStore.HasBlock(sm.databaseContext, selectedChild)
if err != nil {
return nil, err
}
if !hasBlock {
foundHeaderOnlyBlock = true
break
}
lowHash = selectedChild
}
if !foundHeaderOnlyBlock {
// TODO: Once block children are fixed, this error
// should be returned instead of simply logged
log.Errorf("no header-only blocks between %s and %s",
lowHash, highHash)
}
hashesBetween, err := sm.antiPastHashesBetween(lowHash, highHash, 0)
if err != nil {
return nil, err
}
missingBlocks := make([]*externalapi.DomainHash, 0, len(hashesBetween))
for _, blockHash := range hashesBetween {
blockStatus, err := sm.blockStatusStore.Get(sm.databaseContext, blockHash)
if err != nil {
return nil, err
}
if blockStatus == externalapi.StatusHeaderOnly {
missingBlocks = append(missingBlocks, blockHash)
}
}
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
}