stasatdaglabs 7f899b0d09
[NOD-1579] Improve the IBD mechanism (#1174)
* [NOD-1579] Remove selected tip hash messages.

* [NOD-1579] Start moving IBD stuff into blockrelay.

* [NOD-1579] Rename relaytransactions to transactionrelay.

* [NOD-1579] Move IBD files into blockrelay.

* [NOD-1579] Remove flow stuff from ibd.go.

* [NOD-1579] Bring back IsInIBD().

* [NOD-1579] Simplify block relay flow.

* [NOD-1579] Check orphan pool for missing parents to avoid unnecessary processing.

* [NOD-1579] Implement processOrphan.

* [NOD-1579] Implement addToOrphanSetAndRequestMissingParents.

* [NOD-1579] Fix TestIBD.

* [NOD-1579] Implement isBlockInOrphanResolutionRange.

* [NOD-1579] Implement limited block locators.

* [NOD-1579] Add some comments.

* [NOD-1579] Specifically check for StatusHeaderOnly in blockrelay.

* [NOD-1579] Simplify runIBDIfNotRunning.

* [NOD-1579] Don't run IBD if it is already running.

* [NOD-1579] Fix a comment.

* [NOD-1579] Rename mode to syncInfo.

* [NOD-1579] Simplify validateAndInsertBlock.

* [NOD-1579] Fix bad SyncStateSynced condition.

* [NOD-1579] Implement validateAgainstSyncStateAndResolveInsertMode.

* [NOD-1579] Use insertModeHeader.

* [NOD-1579] Add logs to TrySetIBDRunning and UnsetIBDRunning.

* [NOD-1579] Implement and use dequeueIncomingMessageAndSkipInvs.

* [NOD-1579] Fix a log.

* [NOD-1579] Fix a bug in createBlockLocator.

* [NOD-1579] Rename a variable.

* [NOD-1579] Fix a slew of bugs in missingBlockBodyHashes and selectedChildIterator.

* [NOD-1579] Fix bad chunk size in syncMissingBlockBodies.

* [NOD-1579] Remove maxOrphanBlueScoreDiff.

* [NOD-1579] Fix merge errors.

* [NOD-1579] Remove a debug log.

* [NOD-1579] Add logs.

* [NOD-1579] Make various go quality tools happy.

* [NOD-1579] Fix a typo in a variable name.

* [NOD-1579] Fix full blocks over header-only blocks not failing the missing-parents validation.

* [NOD-1579] Add an error log about a condition that should never happen.

* [NOD-1579] Check all antiPast hashes instead of just the lowHash's anticone to filter for header-only blocks.

* [NOD-1579] Remove the nil stuff from GetBlockLocator.

* [NOD-1579] Remove superfluous condition in handleRelayInvsFlow.start().

* [NOD-1579] Return a boolean from requestBlock instead of comparing to nil.

* [NOD-1579] Fix a bad log.Debugf.

* [NOD-1579] Remove a redundant check.

* [NOD-1579] Change an info log to a warning log.

* [NOD-1579] Move OnNewBlock out of relayBlock.

* [NOD-1579] Remove redundant exists check from runIBDIfNotRunning.

* [NOD-1579] Fix bad call to OnNewBlock.

* [NOD-1579] Remove an impossible check.

* [NOD-1579] Added a log.

* [NOD-1579] Rename insertModeBlockWithoutUpdatingVirtual to insertModeBlockBody.

* [NOD-1579] Add a check for duplicate headers.

* [NOD-1579] Added a comment.

* [NOD-1579] Tighten a stop condition.

* [NOD-1579] Simplify a log.

* [NOD-1579] Clarify a log.

* [NOD-1579] Move a log.
2020-12-06 16:23:56 +02:00

191 lines
5.5 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"
)
const maxHashesInAntiPastHashesBetween = 1 << 17
// antiPastHashesBetween returns the hashes of the blocks between the
// lowHash's antiPast and highHash's antiPast, or up to
// maxHashesInAntiPastHashesBetween.
func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
if err != nil {
return nil, err
}
lowBlockBlueScore := lowBlockGHOSTDAGData.BlueScore()
highBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, highHash)
if err != nil {
return nil, err
}
highBlockBlueScore := highBlockGHOSTDAGData.BlueScore()
if lowBlockBlueScore >= highBlockBlueScore {
return nil, errors.Errorf("low hash blueScore >= high hash blueScore (%d >= %d)",
lowBlockBlueScore, highBlockBlueScore)
}
// In order to get no more then maxHashesInAntiPastHashesBetween
// blocks from th future of the lowHash (including itself),
// we iterate the selected parent chain of the highNode and
// stop once we reach
// highBlockBlueScore-lowBlockBlueScore+1 <= maxHashesInAntiPastHashesBetween.
// 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.
for highBlockBlueScore-lowBlockBlueScore+1 > maxHashesInAntiPastHashesBetween {
highHash = highBlockGHOSTDAGData.SelectedParent()
}
// 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()
candidateHashes := 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 = candidateHashes.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
}
}
}
// Pop candidateHashes into a slice. Since candidateHashes is
// an up-heap, it's guaranteed to be ordered from low to high
hashesLength := maxHashesInAntiPastHashesBetween
if candidateHashes.Len() < hashesLength {
hashesLength = candidateHashes.Len()
}
hashes := make([]*externalapi.DomainHash, hashesLength)
for i := 0; i < hashesLength; i++ {
hashes[i] = candidateHashes.Pop()
}
return hashes, nil
}
func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
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
foundHeaderOnlyBlock := false
for selectedChildIterator.Next() {
selectedChild := selectedChildIterator.Get()
selectedChildStatus, err := sm.blockStatusStore.Get(sm.databaseContext, selectedChild)
if err != nil {
return nil, err
}
if selectedChildStatus == externalapi.StatusHeaderOnly {
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)
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
}
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)
}