diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 2d0807194862..34f267736637 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -20,6 +20,7 @@ package downloader import ( "errors" "fmt" + "math" "math/big" "sync" "sync/atomic" @@ -81,6 +82,7 @@ var ( errTimeout = errors.New("timeout") errEmptyHeaderSet = errors.New("empty header set by peer") errPeersUnavailable = errors.New("no peers available or all tried for download") + errNoAncestor = errors.New("no common ancestor") errInvalidAncestor = errors.New("retrieved ancestor is invalid") errInvalidChain = errors.New("retrieved hash chain is invalid") errInvalidBody = errors.New("retrieved block body is invalid") @@ -440,10 +442,16 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I } height := latest.Number.Uint64() - origin, err := d.findAncestor(p, latest) - if err != nil { - return err + var origin = uint64(0) + + if (d.mode != LightSync && d.blockchain.CurrentHeader().Number.Uint64() > 0) || + d.lightchain.CurrentHeader().Number.Uint64() > 0 { + origin, err = d.findAncestor(p, latest) + if err != nil { + return err + } } + d.syncStatsLock.Lock() if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin { d.syncStatsChainOrigin = origin @@ -711,8 +719,10 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) localHeight = d.blockchain.CurrentBlock().NumberU64() case FastSync: localHeight = d.blockchain.CurrentFastBlock().NumberU64() - default: + case LightSync: localHeight = d.lightchain.CurrentHeader().Number.Uint64() + default: + log.Crit("unknown sync mode", "mode", d.mode) } p.log.Debug("Looking for common ancestor", "local", localHeight, "remote", remoteHeight) @@ -741,9 +751,37 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) } } + // Only attempt span search if local head is "close" to reported remote + if math.Abs(float64(remoteHeight-localHeight)) <= float64(MaxHeaderFetch) { + a, err := d.findAncestorSpanSearch(p, remoteHeight, localHeight, floor) + if err == nil { + if a > localHeight { + a = localHeight + } + return a, nil + } + if err != errNoAncestor { + return 0, err + } + } + + // Ancestor not found, we need to binary search over our chain + a, err := d.findAncestorBinarySearch(p, remoteHeight, floor) + if err != nil { + return 0, err + } + if a > localHeight { + a = localHeight + } + return a, nil +} + +// findAncestorSpanSearch looks for a common ancestor by requesting a spaced set of headers between ours and theirs. +func (d *Downloader) findAncestorSpanSearch(p *peerConnection, remoteHeight, localHeight uint64, floor int64) (uint64, error) { from, count, skip, max := calculateRequestSpan(remoteHeight, localHeight) p.log.Trace("Span searching for common ancestor", "count", count, "from", from, "skip", skip) + go p.peer.RequestHeadersByNumber(uint64(from), count, skip, false) // Wait for the remote response to the head fetch @@ -794,8 +832,10 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) known = d.blockchain.HasBlock(h, n) case FastSync: known = d.blockchain.HasFastBlock(h, n) - default: + case LightSync: known = d.lightchain.HasHeader(h, n) + default: + log.Crit("unknown sync mode", "mode", d.mode) } if known { number, hash = n, h @@ -821,13 +861,19 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) p.log.Debug("Found common ancestor", "number", number, "hash", hash) return number, nil } - // Ancestor not found, we need to binary search over our chain + return 0, errNoAncestor +} + +// findAncestorBinarySearch negotiates a binary search using their reported chain. +func (d *Downloader) findAncestorBinarySearch(p *peerConnection, remoteHeight uint64, floor int64) (uint64, error) { start, end := uint64(0), remoteHeight if floor > 0 { start = uint64(floor) } p.log.Trace("Binary searching for common ancestor", "start", start, "end", end) + // Wait for the remote response to the head fetch + hash := common.Hash{} for start+1 < end { // Split our chain interval in two, and request the hash to cross check check := (start + end) / 2 @@ -867,8 +913,10 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) known = d.blockchain.HasBlock(h, n) case FastSync: known = d.blockchain.HasFastBlock(h, n) - default: + case LightSync: known = d.lightchain.HasHeader(h, n) + default: + log.Crit("unknown sync mode", "mode", d.mode) } if !known { end = check