Skip to content

Commit a5eee8d

Browse files
authored
eth/downloader: more context in errors (#21067)
This PR makes use of go 1.13 error handling, wrapping errors and using errors.Is to check a wrapped root-cause. It also removes the travis builders for go 1.11 and go 1.12.
1 parent 389da6a commit a5eee8d

File tree

4 files changed

+49
-47
lines changed

4 files changed

+49
-47
lines changed

.travis.yml

-20
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,6 @@ jobs:
2424
script:
2525
- go run build/ci.go lint
2626

27-
- stage: build
28-
os: linux
29-
dist: xenial
30-
go: 1.11.x
31-
env:
32-
- GO111MODULE=on
33-
script:
34-
- go run build/ci.go install
35-
- go run build/ci.go test -coverage $TEST_PACKAGES
36-
37-
- stage: build
38-
os: linux
39-
dist: xenial
40-
go: 1.12.x
41-
env:
42-
- GO111MODULE=on
43-
script:
44-
- go run build/ci.go install
45-
- go run build/ci.go test -coverage $TEST_PACKAGES
46-
4727
- stage: build
4828
os: linux
4929
dist: xenial

eth/downloader/downloader.go

+25-10
Original file line numberDiff line numberDiff line change
@@ -321,13 +321,28 @@ func (d *Downloader) UnregisterPeer(id string) error {
321321
// adding various sanity checks as well as wrapping it with various log entries.
322322
func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error {
323323
err := d.synchronise(id, head, td, mode)
324+
324325
switch err {
325-
case nil:
326-
case errBusy, errCanceled:
326+
case nil, errBusy, errCanceled:
327+
return err
328+
}
327329

330+
if errors.Is(err, errInvalidChain) {
331+
log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
332+
if d.dropPeer == nil {
333+
// The dropPeer method is nil when `--copydb` is used for a local copy.
334+
// Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
335+
log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", id)
336+
} else {
337+
d.dropPeer(id)
338+
}
339+
return err
340+
}
341+
342+
switch err {
328343
case errTimeout, errBadPeer, errStallingPeer, errUnsyncedPeer,
329344
errEmptyHeaderSet, errPeersUnavailable, errTooOld,
330-
errInvalidAncestor, errInvalidChain:
345+
errInvalidAncestor:
331346
log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
332347
if d.dropPeer == nil {
333348
// The dropPeer method is nil when `--copydb` is used for a local copy.
@@ -774,7 +789,7 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header)
774789
expectNumber := from + int64(i)*int64(skip+1)
775790
if number := header.Number.Int64(); number != expectNumber {
776791
p.log.Warn("Head headers broke chain ordering", "index", i, "requested", expectNumber, "received", number)
777-
return 0, errInvalidChain
792+
return 0, fmt.Errorf("%w: %v", errInvalidChain, errors.New("head headers broke chain ordering"))
778793
}
779794
}
780795
// Check if a common ancestor was found
@@ -988,7 +1003,7 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64)
9881003
filled, proced, err := d.fillHeaderSkeleton(from, headers)
9891004
if err != nil {
9901005
p.log.Debug("Skeleton chain invalid", "err", err)
991-
return errInvalidChain
1006+
return fmt.Errorf("%w: %v", errInvalidChain, err)
9921007
}
9931008
headers = filled[proced:]
9941009
from += uint64(proced)
@@ -1207,13 +1222,13 @@ func (d *Downloader) fetchParts(deliveryCh chan dataPack, deliver func(dataPack)
12071222
if peer := d.peers.Peer(packet.PeerId()); peer != nil {
12081223
// Deliver the received chunk of data and check chain validity
12091224
accepted, err := deliver(packet)
1210-
if err == errInvalidChain {
1225+
if errors.Is(err, errInvalidChain) {
12111226
return err
12121227
}
12131228
// Unless a peer delivered something completely else than requested (usually
12141229
// caused by a timed out request which came through in the end), set it to
12151230
// idle. If the delivery's stale, the peer should have already been idled.
1216-
if err != errStaleDelivery {
1231+
if !errors.Is(err, errStaleDelivery) {
12171232
setIdle(peer, accepted)
12181233
}
12191234
// Issue a log to the user to see what's going on
@@ -1473,7 +1488,7 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
14731488
rollback = append(rollback, chunk[:n]...)
14741489
}
14751490
log.Debug("Invalid header encountered", "number", chunk[n].Number, "hash", chunk[n].Hash(), "err", err)
1476-
return errInvalidChain
1491+
return fmt.Errorf("%w: %v", errInvalidChain, err)
14771492
}
14781493
// All verifications passed, store newly found uncertain headers
14791494
rollback = append(rollback, unknown...)
@@ -1565,7 +1580,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
15651580
// of the blocks delivered from the downloader, and the indexing will be off.
15661581
log.Debug("Downloaded item processing failed on sidechain import", "index", index, "err", err)
15671582
}
1568-
return errInvalidChain
1583+
return fmt.Errorf("%w: %v", errInvalidChain, err)
15691584
}
15701585
return nil
15711586
}
@@ -1706,7 +1721,7 @@ func (d *Downloader) commitFastSyncData(results []*fetchResult, stateSync *state
17061721
}
17071722
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil {
17081723
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
1709-
return errInvalidChain
1724+
return fmt.Errorf("%w: %v", errInvalidChain, err)
17101725
}
17111726
return nil
17121727
}

eth/downloader/downloader_test.go

+16-11
Original file line numberDiff line numberDiff line change
@@ -242,27 +242,32 @@ func (dl *downloadTester) GetTd(hash common.Hash, number uint64) *big.Int {
242242
func (dl *downloadTester) InsertHeaderChain(headers []*types.Header, checkFreq int) (i int, err error) {
243243
dl.lock.Lock()
244244
defer dl.lock.Unlock()
245-
246245
// Do a quick check, as the blockchain.InsertHeaderChain doesn't insert anything in case of errors
247246
if _, ok := dl.ownHeaders[headers[0].ParentHash]; !ok {
248-
return 0, errors.New("unknown parent")
247+
return 0, errors.New("InsertHeaderChain: unknown parent at first position")
249248
}
249+
var hashes []common.Hash
250250
for i := 1; i < len(headers); i++ {
251+
hash := headers[i-1].Hash()
251252
if headers[i].ParentHash != headers[i-1].Hash() {
252-
return i, errors.New("unknown parent")
253+
return i, fmt.Errorf("non-contiguous import at position %d", i)
253254
}
255+
hashes = append(hashes, hash)
254256
}
257+
hashes = append(hashes, headers[len(headers)-1].Hash())
255258
// Do a full insert if pre-checks passed
256259
for i, header := range headers {
257-
if _, ok := dl.ownHeaders[header.Hash()]; ok {
260+
hash := hashes[i]
261+
if _, ok := dl.ownHeaders[hash]; ok {
258262
continue
259263
}
260264
if _, ok := dl.ownHeaders[header.ParentHash]; !ok {
261-
return i, errors.New("unknown parent")
265+
// This _should_ be impossible, due to precheck and induction
266+
return i, fmt.Errorf("InsertHeaderChain: unknown parent at position %d", i)
262267
}
263-
dl.ownHashes = append(dl.ownHashes, header.Hash())
264-
dl.ownHeaders[header.Hash()] = header
265-
dl.ownChainTd[header.Hash()] = new(big.Int).Add(dl.ownChainTd[header.ParentHash], header.Difficulty)
268+
dl.ownHashes = append(dl.ownHashes, hash)
269+
dl.ownHeaders[hash] = header
270+
dl.ownChainTd[hash] = new(big.Int).Add(dl.ownChainTd[header.ParentHash], header.Difficulty)
266271
}
267272
return len(headers), nil
268273
}
@@ -274,9 +279,9 @@ func (dl *downloadTester) InsertChain(blocks types.Blocks) (i int, err error) {
274279

275280
for i, block := range blocks {
276281
if parent, ok := dl.ownBlocks[block.ParentHash()]; !ok {
277-
return i, errors.New("unknown parent")
282+
return i, fmt.Errorf("InsertChain: unknown parent at position %d / %d", i, len(blocks))
278283
} else if _, err := dl.stateDb.Get(parent.Root().Bytes()); err != nil {
279-
return i, fmt.Errorf("unknown parent state %x: %v", parent.Root(), err)
284+
return i, fmt.Errorf("InsertChain: unknown parent state %x: %v", parent.Root(), err)
280285
}
281286
if _, ok := dl.ownHeaders[block.Hash()]; !ok {
282287
dl.ownHashes = append(dl.ownHashes, block.Hash())
@@ -301,7 +306,7 @@ func (dl *downloadTester) InsertReceiptChain(blocks types.Blocks, receipts []typ
301306
}
302307
if _, ok := dl.ancientBlocks[blocks[i].ParentHash()]; !ok {
303308
if _, ok := dl.ownBlocks[blocks[i].ParentHash()]; !ok {
304-
return i, errors.New("unknown parent")
309+
return i, errors.New("InsertReceiptChain: unknown parent")
305310
}
306311
}
307312
if blocks[i].NumberU64() <= ancientLimit {

eth/downloader/queue.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
509509
index := int(header.Number.Int64() - int64(q.resultOffset))
510510
if index >= len(q.resultCache) || index < 0 {
511511
common.Report("index allocation went beyond available resultCache space")
512-
return nil, false, errInvalidChain
512+
return nil, false, fmt.Errorf("%w: index allocation went beyond available resultCache space", errInvalidChain)
513513
}
514514
if q.resultCache[index] == nil {
515515
components := 1
@@ -863,14 +863,16 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
863863
q.active.Signal()
864864
}
865865
// If none of the data was good, it's a stale delivery
866-
switch {
867-
case failure == nil || failure == errInvalidChain:
866+
if failure == nil {
867+
return accepted, nil
868+
}
869+
if errors.Is(failure, errInvalidChain) {
868870
return accepted, failure
869-
case useful:
871+
}
872+
if useful {
870873
return accepted, fmt.Errorf("partial failure: %v", failure)
871-
default:
872-
return accepted, errStaleDelivery
873874
}
875+
return accepted, fmt.Errorf("%w: %v", failure, errStaleDelivery)
874876
}
875877

876878
// Prepare configures the result cache to allow accepting and caching inbound

0 commit comments

Comments
 (0)