aboutsummaryrefslogtreecommitdiffstats
path: root/eth/downloader/downloader.go
diff options
context:
space:
mode:
Diffstat (limited to 'eth/downloader/downloader.go')
-rw-r--r--eth/downloader/downloader.go667
1 files changed, 69 insertions, 598 deletions
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 01c0818a0..1e9bc27bc 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -48,23 +48,17 @@ var (
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
MaxStateFetch = 384 // Amount of node state values to allow fetching per request
- MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation
-
- hashTTL = 3 * time.Second // [eth/61] Time it takes for a hash request to time out
- blockTargetRTT = 3 * time.Second / 2 // [eth/61] Target time for completing a block retrieval request
- blockTTL = 3 * blockTargetRTT // [eth/61] Maximum time allowance before a block request is considered expired
-
- rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
- rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
- rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
- ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion
- ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts
+ MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation
+ rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
+ rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
+ rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
+ ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion
+ ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts
qosTuningPeers = 5 // Number of peers to tune based on (best peers)
qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence
qosTuningImpact = 0.25 // Impact that a new tuning target has on the previous value
- maxQueuedHashes = 32 * 1024 // [eth/61] Maximum number of hashes to queue for import (DOS protection)
maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection)
maxHeadersProcess = 2048 // Number of header download results to import at once into the chain
maxResultsProcess = 2048 // Number of content download results to import at once into the chain
@@ -84,16 +78,13 @@ var (
errStallingPeer = errors.New("peer is stalling")
errNoPeers = errors.New("no peers to keep download active")
errTimeout = errors.New("timeout")
- errEmptyHashSet = errors.New("empty hash set by peer")
errEmptyHeaderSet = errors.New("empty header set by peer")
errPeersUnavailable = errors.New("no peers available or all tried for download")
- errAlreadyInPool = errors.New("hash already in pool")
errInvalidAncestor = errors.New("retrieved ancestor is invalid")
errInvalidChain = errors.New("retrieved hash chain is invalid")
errInvalidBlock = errors.New("retrieved block is invalid")
errInvalidBody = errors.New("retrieved block body is invalid")
errInvalidReceipt = errors.New("retrieved receipt is invalid")
- errCancelHashFetch = errors.New("hash download canceled (requested)")
errCancelBlockFetch = errors.New("block download canceled (requested)")
errCancelHeaderFetch = errors.New("block header download canceled (requested)")
errCancelBodyFetch = errors.New("block body download canceled (requested)")
@@ -102,6 +93,7 @@ var (
errCancelHeaderProcessing = errors.New("header processing canceled (requested)")
errCancelContentProcessing = errors.New("content processing canceled (requested)")
errNoSyncActive = errors.New("no sync active")
+ errTooOld = errors.New("peer doesn't speak recent enough protocol version (need version >= 62)")
)
type Downloader struct {
@@ -146,13 +138,10 @@ type Downloader struct {
// Channels
newPeerCh chan *peer
- hashCh chan dataPack // [eth/61] Channel receiving inbound hashes
- blockCh chan dataPack // [eth/61] Channel receiving inbound blocks
headerCh chan dataPack // [eth/62] Channel receiving inbound block headers
bodyCh chan dataPack // [eth/62] Channel receiving inbound block bodies
receiptCh chan dataPack // [eth/63] Channel receiving inbound receipts
stateCh chan dataPack // [eth/63] Channel receiving inbound node state data
- blockWakeCh chan bool // [eth/61] Channel to signal the block fetcher of new tasks
bodyWakeCh chan bool // [eth/62] Channel to signal the block body fetcher of new tasks
receiptWakeCh chan bool // [eth/63] Channel to signal the receipt fetcher of new tasks
stateWakeCh chan bool // [eth/63] Channel to signal the state fetcher of new tasks
@@ -199,13 +188,10 @@ func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, ha
rollback: rollback,
dropPeer: dropPeer,
newPeerCh: make(chan *peer, 1),
- hashCh: make(chan dataPack, 1),
- blockCh: make(chan dataPack, 1),
headerCh: make(chan dataPack, 1),
bodyCh: make(chan dataPack, 1),
receiptCh: make(chan dataPack, 1),
stateCh: make(chan dataPack, 1),
- blockWakeCh: make(chan bool, 1),
bodyWakeCh: make(chan bool, 1),
receiptWakeCh: make(chan bool, 1),
stateWakeCh: make(chan bool, 1),
@@ -251,12 +237,11 @@ func (d *Downloader) Synchronising() bool {
// RegisterPeer injects a new download peer into the set of block source to be
// used for fetching hashes and blocks from.
func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
- getRelHashes relativeHashFetcherFn, getAbsHashes absoluteHashFetcherFn, getBlocks blockFetcherFn, // eth/61 callbacks, remove when upgrading
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error {
glog.V(logger.Detail).Infoln("Registering peer", id)
- if err := d.peers.Register(newPeer(id, version, head, getRelHashes, getAbsHashes, getBlocks, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
+ if err := d.peers.Register(newPeer(id, version, head, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
glog.V(logger.Error).Infoln("Register failed:", err)
return err
}
@@ -291,7 +276,9 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
case errBusy:
glog.V(logger.Detail).Infof("Synchronisation already in progress")
- case errTimeout, errBadPeer, errStallingPeer, errEmptyHashSet, errEmptyHeaderSet, errPeersUnavailable, errInvalidAncestor, errInvalidChain:
+ case errTimeout, errBadPeer, errStallingPeer,
+ errEmptyHeaderSet, errPeersUnavailable, errTooOld,
+ errInvalidAncestor, errInvalidChain:
glog.V(logger.Debug).Infof("Removing peer %v: %v", id, err)
d.dropPeer(id)
@@ -323,13 +310,13 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
d.queue.Reset()
d.peers.Reset()
- for _, ch := range []chan bool{d.blockWakeCh, d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} {
+ for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} {
select {
case <-ch:
default:
}
}
- for _, ch := range []chan dataPack{d.hashCh, d.blockCh, d.headerCh, d.bodyCh, d.receiptCh, d.stateCh} {
+ for _, ch := range []chan dataPack{d.headerCh, d.bodyCh, d.receiptCh, d.stateCh} {
for empty := false; !empty; {
select {
case <-ch:
@@ -377,105 +364,73 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
d.mux.Post(DoneEvent{})
}
}()
+ if p.version < 62 {
+ return errTooOld
+ }
glog.V(logger.Debug).Infof("Synchronising with the network using: %s [eth/%d]", p.id, p.version)
defer func(start time.Time) {
glog.V(logger.Debug).Infof("Synchronisation terminated after %v", time.Since(start))
}(time.Now())
- switch {
- case p.version == 61:
- // Look up the sync boundaries: the common ancestor and the target block
- latest, err := d.fetchHeight61(p)
- if err != nil {
- return err
- }
- origin, err := d.findAncestor61(p, latest)
- if err != nil {
- return err
- }
- d.syncStatsLock.Lock()
- if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
- d.syncStatsChainOrigin = origin
- }
- d.syncStatsChainHeight = latest
- d.syncStatsLock.Unlock()
+ // Look up the sync boundaries: the common ancestor and the target block
+ latest, err := d.fetchHeight(p)
+ if err != nil {
+ return err
+ }
+ height := latest.Number.Uint64()
- // Initiate the sync using a concurrent hash and block retrieval algorithm
- d.queue.Prepare(origin+1, d.mode, 0, nil)
- if d.syncInitHook != nil {
- d.syncInitHook(origin, latest)
- }
- return d.spawnSync(origin+1,
- func() error { return d.fetchHashes61(p, td, origin+1) },
- func() error { return d.fetchBlocks61(origin + 1) },
- )
-
- case p.version >= 62:
- // Look up the sync boundaries: the common ancestor and the target block
- latest, err := d.fetchHeight(p)
- if err != nil {
- return err
- }
- height := latest.Number.Uint64()
+ origin, err := d.findAncestor(p, height)
+ if err != nil {
+ return err
+ }
+ d.syncStatsLock.Lock()
+ if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
+ d.syncStatsChainOrigin = origin
+ }
+ d.syncStatsChainHeight = height
+ d.syncStatsLock.Unlock()
- origin, err := d.findAncestor(p, height)
- if err != nil {
- return err
- }
- d.syncStatsLock.Lock()
- if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
- d.syncStatsChainOrigin = origin
- }
- d.syncStatsChainHeight = height
- d.syncStatsLock.Unlock()
-
- // Initiate the sync using a concurrent header and content retrieval algorithm
- pivot := uint64(0)
- switch d.mode {
- case LightSync:
- pivot = height
- case FastSync:
- // Calculate the new fast/slow sync pivot point
- if d.fsPivotLock == nil {
- pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
- if err != nil {
- panic(fmt.Sprintf("Failed to access crypto random source: %v", err))
- }
- if height > uint64(fsMinFullBlocks)+pivotOffset.Uint64() {
- pivot = height - uint64(fsMinFullBlocks) - pivotOffset.Uint64()
- }
- } else {
- // Pivot point locked in, use this and do not pick a new one!
- pivot = d.fsPivotLock.Number.Uint64()
+ // Initiate the sync using a concurrent header and content retrieval algorithm
+ pivot := uint64(0)
+ switch d.mode {
+ case LightSync:
+ pivot = height
+ case FastSync:
+ // Calculate the new fast/slow sync pivot point
+ if d.fsPivotLock == nil {
+ pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
+ if err != nil {
+ panic(fmt.Sprintf("Failed to access crypto random source: %v", err))
}
- // If the point is below the origin, move origin back to ensure state download
- if pivot < origin {
- if pivot > 0 {
- origin = pivot - 1
- } else {
- origin = 0
- }
+ if height > uint64(fsMinFullBlocks)+pivotOffset.Uint64() {
+ pivot = height - uint64(fsMinFullBlocks) - pivotOffset.Uint64()
}
- glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot)
+ } else {
+ // Pivot point locked in, use this and do not pick a new one!
+ pivot = d.fsPivotLock.Number.Uint64()
}
- d.queue.Prepare(origin+1, d.mode, pivot, latest)
- if d.syncInitHook != nil {
- d.syncInitHook(origin, height)
+ // If the point is below the origin, move origin back to ensure state download
+ if pivot < origin {
+ if pivot > 0 {
+ origin = pivot - 1
+ } else {
+ origin = 0
+ }
}
- return d.spawnSync(origin+1,
- func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved
- func() error { return d.processHeaders(origin+1, td) }, // Headers are always retrieved
- func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
- func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
- func() error { return d.fetchNodeData() }, // Node state data is retrieved during fast sync
- )
-
- default:
- // Something very wrong, stop right here
- glog.V(logger.Error).Infof("Unsupported eth protocol: %d", p.version)
- return errBadPeer
+ glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot)
+ }
+ d.queue.Prepare(origin+1, d.mode, pivot, latest)
+ if d.syncInitHook != nil {
+ d.syncInitHook(origin, height)
}
+ return d.spawnSync(origin+1,
+ func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved
+ func() error { return d.processHeaders(origin+1, td) }, // Headers are always retrieved
+ func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
+ func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
+ func() error { return d.fetchNodeData() }, // Node state data is retrieved during fast sync
+ )
}
// spawnSync runs d.process and all given fetcher functions to completion in
@@ -540,452 +495,6 @@ func (d *Downloader) Terminate() {
d.cancel()
}
-// fetchHeight61 retrieves the head block of the remote peer to aid in estimating
-// the total time a pending synchronisation would take.
-func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
- glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
-
- // Request the advertised remote head block and wait for the response
- go p.getBlocks([]common.Hash{p.head})
-
- timeout := time.After(hashTTL)
- for {
- select {
- case <-d.cancelCh:
- return 0, errCancelBlockFetch
-
- case packet := <-d.blockCh:
- // Discard anything not from the origin peer
- if packet.PeerId() != p.id {
- glog.V(logger.Debug).Infof("Received blocks from incorrect peer(%s)", packet.PeerId())
- break
- }
- // Make sure the peer actually gave something valid
- blocks := packet.(*blockPack).blocks
- if len(blocks) != 1 {
- glog.V(logger.Debug).Infof("%v: invalid number of head blocks: %d != 1", p, len(blocks))
- return 0, errBadPeer
- }
- return blocks[0].NumberU64(), nil
-
- case <-timeout:
- glog.V(logger.Debug).Infof("%v: head block timeout", p)
- return 0, errTimeout
-
- case <-d.hashCh:
- // Out of bounds hashes received, ignore them
-
- case <-d.headerCh:
- case <-d.bodyCh:
- case <-d.stateCh:
- case <-d.receiptCh:
- // Ignore eth/{62,63} packets because this is eth/61.
- // These can arrive as a late delivery from a previous sync.
- }
- }
-}
-
-// findAncestor61 tries to locate the common ancestor block of the local chain and
-// a remote peers blockchain. In the general case when our node was in sync and
-// on the correct chain, checking the top N blocks should already get us a match.
-// In the rare scenario when we ended up on a long reorganisation (i.e. none of
-// the head blocks match), we do a binary search to find the common ancestor.
-func (d *Downloader) findAncestor61(p *peer, height uint64) (uint64, error) {
- glog.V(logger.Debug).Infof("%v: looking for common ancestor", p)
-
- // Figure out the valid ancestor range to prevent rewrite attacks
- floor, ceil := int64(-1), d.headBlock().NumberU64()
- if ceil >= MaxForkAncestry {
- floor = int64(ceil - MaxForkAncestry)
- }
- // Request the topmost blocks to short circuit binary ancestor lookup
- head := ceil
- if head > height {
- head = height
- }
- from := int64(head) - int64(MaxHashFetch) + 1
- if from < 0 {
- from = 0
- }
- go p.getAbsHashes(uint64(from), MaxHashFetch)
-
- // Wait for the remote response to the head fetch
- number, hash := uint64(0), common.Hash{}
- timeout := time.After(hashTTL)
-
- for finished := false; !finished; {
- select {
- case <-d.cancelCh:
- return 0, errCancelHashFetch
-
- case packet := <-d.hashCh:
- // Discard anything not from the origin peer
- if packet.PeerId() != p.id {
- glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
- break
- }
- // Make sure the peer actually gave something valid
- hashes := packet.(*hashPack).hashes
- if len(hashes) == 0 {
- glog.V(logger.Debug).Infof("%v: empty head hash set", p)
- return 0, errEmptyHashSet
- }
- // Check if a common ancestor was found
- finished = true
- for i := len(hashes) - 1; i >= 0; i-- {
- // Skip any headers that underflow/overflow our requested set
- header := d.getHeader(hashes[i])
- if header == nil || header.Number.Int64() < from || header.Number.Uint64() > head {
- continue
- }
- // Otherwise check if we already know the header or not
- if d.hasBlockAndState(hashes[i]) {
- number, hash = header.Number.Uint64(), header.Hash()
- break
- }
- }
-
- case <-timeout:
- glog.V(logger.Debug).Infof("%v: head hash timeout", p)
- return 0, errTimeout
-
- case <-d.blockCh:
- // Out of bounds blocks received, ignore them
-
- case <-d.headerCh:
- case <-d.bodyCh:
- case <-d.stateCh:
- case <-d.receiptCh:
- // Ignore eth/{62,63} packets because this is eth/61.
- // These can arrive as a late delivery from a previous sync.
- }
- }
- // If the head fetch already found an ancestor, return
- if !common.EmptyHash(hash) {
- if int64(number) <= floor {
- glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, number, hash[:4], floor)
- return 0, errInvalidAncestor
- }
- glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, number, hash[:4])
- return number, nil
- }
- // Ancestor not found, we need to binary search over our chain
- start, end := uint64(0), head
- if floor > 0 {
- start = uint64(floor)
- }
- for start+1 < end {
- // Split our chain interval in two, and request the hash to cross check
- check := (start + end) / 2
-
- timeout := time.After(hashTTL)
- go p.getAbsHashes(uint64(check), 1)
-
- // Wait until a reply arrives to this request
- for arrived := false; !arrived; {
- select {
- case <-d.cancelCh:
- return 0, errCancelHashFetch
-
- case packet := <-d.hashCh:
- // Discard anything not from the origin peer
- if packet.PeerId() != p.id {
- glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
- break
- }
- // Make sure the peer actually gave something valid
- hashes := packet.(*hashPack).hashes
- if len(hashes) != 1 {
- glog.V(logger.Debug).Infof("%v: invalid search hash set (%d)", p, len(hashes))
- return 0, errBadPeer
- }
- arrived = true
-
- // Modify the search interval based on the response
- if !d.hasBlockAndState(hashes[0]) {
- end = check
- break
- }
- block := d.getBlock(hashes[0]) // this doesn't check state, hence the above explicit check
- if block.NumberU64() != check {
- glog.V(logger.Debug).Infof("%v: non requested hash #%d [%x…], instead of #%d", p, block.NumberU64(), block.Hash().Bytes()[:4], check)
- return 0, errBadPeer
- }
- start = check
-
- case <-timeout:
- glog.V(logger.Debug).Infof("%v: search hash timeout", p)
- return 0, errTimeout
-
- case <-d.blockCh:
- // Out of bounds blocks received, ignore them
-
- case <-d.headerCh:
- case <-d.bodyCh:
- case <-d.stateCh:
- case <-d.receiptCh:
- // Ignore eth/{62,63} packets because this is eth/61.
- // These can arrive as a late delivery from a previous sync.
- }
- }
- }
- // Ensure valid ancestry and return
- if int64(start) <= floor {
- glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, start, hash[:4], floor)
- return 0, errInvalidAncestor
- }
- glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, start, hash[:4])
- return start, nil
-}
-
-// fetchHashes61 keeps retrieving hashes from the requested number, until no more
-// are returned, potentially throttling on the way.
-func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
- glog.V(logger.Debug).Infof("%v: downloading hashes from #%d", p, from)
-
- // Create a timeout timer, and the associated hash fetcher
- request := time.Now() // time of the last fetch request
- timeout := time.NewTimer(0) // timer to dump a non-responsive active peer
- <-timeout.C // timeout channel should be initially empty
- defer timeout.Stop()
-
- getHashes := func(from uint64) {
- glog.V(logger.Detail).Infof("%v: fetching %d hashes from #%d", p, MaxHashFetch, from)
-
- request = time.Now()
- timeout.Reset(hashTTL)
- go p.getAbsHashes(from, MaxHashFetch)
- }
- // Start pulling hashes, until all are exhausted
- getHashes(from)
- gotHashes := false
-
- for {
- select {
- case <-d.cancelCh:
- return errCancelHashFetch
-
- case packet := <-d.hashCh:
- // Make sure the active peer is giving us the hashes
- if packet.PeerId() != p.id {
- glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
- break
- }
- hashReqTimer.UpdateSince(request)
- timeout.Stop()
-
- // If no more hashes are inbound, notify the block fetcher and return
- if packet.Items() == 0 {
- glog.V(logger.Debug).Infof("%v: no available hashes", p)
-
- select {
- case d.blockWakeCh <- false:
- case <-d.cancelCh:
- }
- // If no hashes were retrieved at all, the peer violated it's TD promise that it had a
- // better chain compared to ours. The only exception is if it's promised blocks were
- // already imported by other means (e.g. fetcher):
- //
- // R <remote peer>, L <local node>: Both at block 10
- // R: Mine block 11, and propagate it to L
- // L: Queue block 11 for import
- // L: Notice that R's head and TD increased compared to ours, start sync
- // L: Import of block 11 finishes
- // L: Sync begins, and finds common ancestor at 11
- // L: Request new hashes up from 11 (R's TD was higher, it must have something)
- // R: Nothing to give
- if !gotHashes && td.Cmp(d.getTd(d.headBlock().Hash())) > 0 {
- return errStallingPeer
- }
- return nil
- }
- gotHashes = true
- hashes := packet.(*hashPack).hashes
-
- // Otherwise insert all the new hashes, aborting in case of junk
- glog.V(logger.Detail).Infof("%v: scheduling %d hashes from #%d", p, len(hashes), from)
-
- inserts := d.queue.Schedule61(hashes, true)
- if len(inserts) != len(hashes) {
- glog.V(logger.Debug).Infof("%v: stale hashes", p)
- return errBadPeer
- }
- // Notify the block fetcher of new hashes, but stop if queue is full
- if d.queue.PendingBlocks() < maxQueuedHashes {
- // We still have hashes to fetch, send continuation wake signal (potential)
- select {
- case d.blockWakeCh <- true:
- default:
- }
- } else {
- // Hash limit reached, send a termination wake signal (enforced)
- select {
- case d.blockWakeCh <- false:
- case <-d.cancelCh:
- }
- return nil
- }
- // Queue not yet full, fetch the next batch
- from += uint64(len(hashes))
- getHashes(from)
-
- case <-timeout.C:
- glog.V(logger.Debug).Infof("%v: hash request timed out", p)
- hashTimeoutMeter.Mark(1)
- return errTimeout
-
- case <-d.headerCh:
- case <-d.bodyCh:
- case <-d.stateCh:
- case <-d.receiptCh:
- // Ignore eth/{62,63} packets because this is eth/61.
- // These can arrive as a late delivery from a previous sync.
- }
- }
-}
-
-// fetchBlocks61 iteratively downloads the scheduled hashes, taking any available
-// peers, reserving a chunk of blocks for each, waiting for delivery and also
-// periodically checking for timeouts.
-func (d *Downloader) fetchBlocks61(from uint64) error {
- glog.V(logger.Debug).Infof("Downloading blocks from #%d", from)
- defer glog.V(logger.Debug).Infof("Block download terminated")
-
- // Create a timeout timer for scheduling expiration tasks
- ticker := time.NewTicker(100 * time.Millisecond)
- defer ticker.Stop()
-
- update := make(chan struct{}, 1)
-
- // Fetch blocks until the hash fetcher's done
- finished := false
- for {
- select {
- case <-d.cancelCh:
- return errCancelBlockFetch
-
- case packet := <-d.blockCh:
- // If the peer was previously banned and failed to deliver it's pack
- // in a reasonable time frame, ignore it's message.
- if peer := d.peers.Peer(packet.PeerId()); peer != nil {
- blocks := packet.(*blockPack).blocks
-
- // Deliver the received chunk of blocks and check chain validity
- accepted, err := d.queue.DeliverBlocks(peer.id, blocks)
- if err == errInvalidChain {
- return err
- }
- // Unless a peer delivered something completely else than requested (usually
- // caused by a timed out request which came through in the end), set it to
- // idle. If the delivery's stale, the peer should have already been idled.
- if err != errStaleDelivery {
- peer.SetBlocksIdle(accepted)
- }
- // Issue a log to the user to see what's going on
- switch {
- case err == nil && len(blocks) == 0:
- glog.V(logger.Detail).Infof("%s: no blocks delivered", peer)
- case err == nil:
- glog.V(logger.Detail).Infof("%s: delivered %d blocks", peer, len(blocks))
- default:
- glog.V(logger.Detail).Infof("%s: delivery failed: %v", peer, err)
- }
- }
- // Blocks arrived, try to update the progress
- select {
- case update <- struct{}{}:
- default:
- }
-
- case cont := <-d.blockWakeCh:
- // The hash fetcher sent a continuation flag, check if it's done
- if !cont {
- finished = true
- }
- // Hashes arrive, try to update the progress
- select {
- case update <- struct{}{}:
- default:
- }
-
- case <-ticker.C:
- // Sanity check update the progress
- select {
- case update <- struct{}{}:
- default:
- }
-
- case <-update:
- // Short circuit if we lost all our peers
- if d.peers.Len() == 0 {
- return errNoPeers
- }
- // Check for block request timeouts and demote the responsible peers
- for pid, fails := range d.queue.ExpireBlocks(blockTTL) {
- if peer := d.peers.Peer(pid); peer != nil {
- if fails > 1 {
- glog.V(logger.Detail).Infof("%s: block delivery timeout", peer)
- peer.SetBlocksIdle(0)
- } else {
- glog.V(logger.Debug).Infof("%s: stalling block delivery, dropping", peer)
- d.dropPeer(pid)
- }
- }
- }
- // If there's nothing more to fetch, wait or terminate
- if d.queue.PendingBlocks() == 0 {
- if !d.queue.InFlightBlocks() && finished {
- glog.V(logger.Debug).Infof("Block fetching completed")
- return nil
- }
- break
- }
- // Send a download request to all idle peers, until throttled
- throttled := false
- idles, total := d.peers.BlockIdlePeers()
-
- for _, peer := range idles {
- // Short circuit if throttling activated
- if d.queue.ShouldThrottleBlocks() {
- throttled = true
- break
- }
- // Reserve a chunk of hashes for a peer. A nil can mean either that
- // no more hashes are available, or that the peer is known not to
- // have them.
- request := d.queue.ReserveBlocks(peer, peer.BlockCapacity(blockTargetRTT))
- if request == nil {
- continue
- }
- if glog.V(logger.Detail) {
- glog.Infof("%s: requesting %d blocks", peer, len(request.Hashes))
- }
- // Fetch the chunk and make sure any errors return the hashes to the queue
- if err := peer.Fetch61(request); err != nil {
- // Although we could try and make an attempt to fix this, this error really
- // means that we've double allocated a fetch task to a peer. If that is the
- // case, the internal state of the downloader and the queue is very wrong so
- // better hard crash and note the error instead of silently accumulating into
- // a much bigger issue.
- panic(fmt.Sprintf("%v: fetch assignment failed", peer))
- }
- }
- // Make sure that we have peers available for fetching. If all peers have been tried
- // and all failed throw an error
- if !throttled && !d.queue.InFlightBlocks() && len(idles) == total {
- return errPeersUnavailable
- }
-
- case <-d.headerCh:
- case <-d.bodyCh:
- case <-d.stateCh:
- case <-d.receiptCh:
- // Ignore eth/{62,63} packets because this is eth/61.
- // These can arrive as a late delivery from a previous sync.
- }
- }
-}
-
// fetchHeight retrieves the head header of the remote peer to aid in estimating
// the total time a pending synchronisation would take.
func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
@@ -1022,11 +531,6 @@ func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
case <-d.stateCh:
case <-d.receiptCh:
// Out of bounds delivery, ignore
-
- case <-d.hashCh:
- case <-d.blockCh:
- // Ignore eth/61 packets because this is eth/62+.
- // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1067,7 +571,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
for finished := false; !finished; {
select {
case <-d.cancelCh:
- return 0, errCancelHashFetch
+ return 0, errCancelHeaderFetch
case packet := <-d.headerCh:
// Discard anything not from the origin peer
@@ -1114,11 +618,6 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
case <-d.stateCh:
case <-d.receiptCh:
// Out of bounds delivery, ignore
-
- case <-d.hashCh:
- case <-d.blockCh:
- // Ignore eth/61 packets because this is eth/62+.
- // These can arrive as a late delivery from a previous sync.
}
}
// If the head fetch already found an ancestor, return
@@ -1146,7 +645,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
for arrived := false; !arrived; {
select {
case <-d.cancelCh:
- return 0, errCancelHashFetch
+ return 0, errCancelHeaderFetch
case packer := <-d.headerCh:
// Discard anything not from the origin peer
@@ -1182,11 +681,6 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
case <-d.stateCh:
case <-d.receiptCh:
// Out of bounds delivery, ignore
-
- case <-d.hashCh:
- case <-d.blockCh:
- // Ignore eth/61 packets because this is eth/62+.
- // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1305,11 +799,6 @@ func (d *Downloader) fetchHeaders(p *peer, from uint64) error {
case <-d.cancelCh:
}
return errBadPeer
-
- case <-d.hashCh:
- case <-d.blockCh:
- // Ignore eth/61 packets because this is eth/62+.
- // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1623,11 +1112,6 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
if !progressed && !throttled && !running && len(idles) == total && pending() > 0 {
return errPeersUnavailable
}
-
- case <-d.hashCh:
- case <-d.blockCh:
- // Ignore eth/61 packets because this is eth/62+.
- // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1867,19 +1351,6 @@ func (d *Downloader) processContent() error {
}
}
-// DeliverHashes injects a new batch of hashes received from a remote node into
-// the download schedule. This is usually invoked through the BlockHashesMsg by
-// the protocol handler.
-func (d *Downloader) DeliverHashes(id string, hashes []common.Hash) (err error) {
- return d.deliver(id, d.hashCh, &hashPack{id, hashes}, hashInMeter, hashDropMeter)
-}
-
-// DeliverBlocks injects a new batch of blocks received from a remote node.
-// This is usually invoked through the BlocksMsg by the protocol handler.
-func (d *Downloader) DeliverBlocks(id string, blocks []*types.Block) (err error) {
- return d.deliver(id, d.blockCh, &blockPack{id, blocks}, blockInMeter, blockDropMeter)
-}
-
// DeliverHeaders injects a new batch of block headers received from a remote
// node into the download schedule.
func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) (err error) {