diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 2d08071948..40ee78f90f 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -81,6 +81,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") @@ -711,8 +712,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 +744,36 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) } } + // Only attempt span search if local head is "close" to reported remote + if remoteHeight-localHeight < uint64(MaxHeaderFetch) { + a, err := d.findAncestorSpanSearch(p, remoteHeight, localHeight, floor) + if err == nil { + if a > localHeight { + a = localHeight + } + return a, nil + } else 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 +824,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 +853,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 +905,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