Skip to content

Commit b85c183

Browse files
authored
eth/downloader: remove header rollback mechanism (#28147)
* eth/downloader: remove rollback mechanism in downloader * eth/downloader: remove the tests
1 parent adb9b31 commit b85c183

File tree

2 files changed

+2
-133
lines changed

2 files changed

+2
-133
lines changed

eth/downloader/downloader.go

+2-53
Original file line numberDiff line numberDiff line change
@@ -1280,41 +1280,13 @@ func (d *Downloader) fetchReceipts(from uint64, beaconMode bool) error {
12801280
// keeps processing and scheduling them into the header chain and downloader's
12811281
// queue until the stream ends or a failure occurs.
12821282
func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode bool) error {
1283-
// Keep a count of uncertain headers to roll back
12841283
var (
1285-
rollback uint64 // Zero means no rollback (fine as you can't unroll the genesis)
1286-
rollbackErr error
1287-
mode = d.getMode()
1284+
mode = d.getMode()
1285+
gotHeaders = false // Wait for batches of headers to process
12881286
)
1289-
defer func() {
1290-
if rollback > 0 {
1291-
lastHeader, lastFastBlock, lastBlock := d.lightchain.CurrentHeader().Number, common.Big0, common.Big0
1292-
if mode != LightSync {
1293-
lastFastBlock = d.blockchain.CurrentSnapBlock().Number
1294-
lastBlock = d.blockchain.CurrentBlock().Number
1295-
}
1296-
if err := d.lightchain.SetHead(rollback - 1); err != nil { // -1 to target the parent of the first uncertain block
1297-
// We're already unwinding the stack, only print the error to make it more visible
1298-
log.Error("Failed to roll back chain segment", "head", rollback-1, "err", err)
1299-
}
1300-
curFastBlock, curBlock := common.Big0, common.Big0
1301-
if mode != LightSync {
1302-
curFastBlock = d.blockchain.CurrentSnapBlock().Number
1303-
curBlock = d.blockchain.CurrentBlock().Number
1304-
}
1305-
log.Warn("Rolled back chain segment",
1306-
"header", fmt.Sprintf("%d->%d", lastHeader, d.lightchain.CurrentHeader().Number),
1307-
"snap", fmt.Sprintf("%d->%d", lastFastBlock, curFastBlock),
1308-
"block", fmt.Sprintf("%d->%d", lastBlock, curBlock), "reason", rollbackErr)
1309-
}
1310-
}()
1311-
// Wait for batches of headers to process
1312-
gotHeaders := false
1313-
13141287
for {
13151288
select {
13161289
case <-d.cancelCh:
1317-
rollbackErr = errCanceled
13181290
return errCanceled
13191291

13201292
case task := <-d.headerProcCh:
@@ -1363,8 +1335,6 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
13631335
}
13641336
}
13651337
}
1366-
// Disable any rollback and return
1367-
rollback = 0
13681338
return nil
13691339
}
13701340
// Otherwise split the chunk of headers into batches and process them
@@ -1375,7 +1345,6 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
13751345
// Terminate if something failed in between processing chunks
13761346
select {
13771347
case <-d.cancelCh:
1378-
rollbackErr = errCanceled
13791348
return errCanceled
13801349
default:
13811350
}
@@ -1422,29 +1391,11 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
14221391
}
14231392
if len(chunkHeaders) > 0 {
14241393
if n, err := d.lightchain.InsertHeaderChain(chunkHeaders); err != nil {
1425-
rollbackErr = err
1426-
1427-
// If some headers were inserted, track them as uncertain
1428-
if mode == SnapSync && n > 0 && rollback == 0 {
1429-
rollback = chunkHeaders[0].Number.Uint64()
1430-
}
14311394
log.Warn("Invalid header encountered", "number", chunkHeaders[n].Number, "hash", chunkHashes[n], "parent", chunkHeaders[n].ParentHash, "err", err)
14321395
return fmt.Errorf("%w: %v", errInvalidChain, err)
14331396
}
1434-
// All verifications passed, track all headers within the allowed limits
1435-
if mode == SnapSync {
1436-
head := chunkHeaders[len(chunkHeaders)-1].Number.Uint64()
1437-
if head-rollback > uint64(fsHeaderSafetyNet) {
1438-
rollback = head - uint64(fsHeaderSafetyNet)
1439-
} else {
1440-
rollback = 1
1441-
}
1442-
}
14431397
}
14441398
if len(rejected) != 0 {
1445-
// Merge threshold reached, stop importing, but don't roll back
1446-
rollback = 0
1447-
14481399
log.Info("Legacy sync reached merge threshold", "number", rejected[0].Number, "hash", rejected[0].Hash(), "td", td, "ttd", ttd)
14491400
return ErrMergeTransition
14501401
}
@@ -1455,15 +1406,13 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
14551406
for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
14561407
select {
14571408
case <-d.cancelCh:
1458-
rollbackErr = errCanceled
14591409
return errCanceled
14601410
case <-time.After(time.Second):
14611411
}
14621412
}
14631413
// Otherwise insert the headers for content retrieval
14641414
inserts := d.queue.Schedule(chunkHeaders, chunkHashes, origin)
14651415
if len(inserts) != len(chunkHeaders) {
1466-
rollbackErr = fmt.Errorf("stale headers: len inserts %v len(chunk) %v", len(inserts), len(chunkHeaders))
14671416
return fmt.Errorf("%w: stale headers", errBadPeer)
14681417
}
14691418
}

eth/downloader/downloader_test.go

-80
Original file line numberDiff line numberDiff line change
@@ -878,86 +878,6 @@ func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
878878
assertOwnChain(t, tester, len(chain.blocks))
879879
}
880880

881-
// Tests that upon detecting an invalid header, the recent ones are rolled back
882-
// for various failure scenarios. Afterwards a full sync is attempted to make
883-
// sure no state was corrupted.
884-
func TestInvalidHeaderRollback66Snap(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, SnapSync) }
885-
func TestInvalidHeaderRollback67Snap(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH67, SnapSync) }
886-
887-
func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) {
888-
tester := newTester(t)
889-
defer tester.terminate()
890-
891-
// Create a small enough block chain to download
892-
targetBlocks := 3*fsHeaderSafetyNet + 256 + fsMinFullBlocks
893-
chain := testChainBase.shorten(targetBlocks)
894-
895-
// Attempt to sync with an attacker that feeds junk during the fast sync phase.
896-
// This should result in the last fsHeaderSafetyNet headers being rolled back.
897-
missing := fsHeaderSafetyNet + MaxHeaderFetch + 1
898-
899-
fastAttacker := tester.newPeer("fast-attack", protocol, chain.blocks[1:])
900-
fastAttacker.withholdHeaders[chain.blocks[missing].Hash()] = struct{}{}
901-
902-
if err := tester.sync("fast-attack", nil, mode); err == nil {
903-
t.Fatalf("succeeded fast attacker synchronisation")
904-
}
905-
if head := tester.chain.CurrentHeader().Number.Int64(); int(head) > MaxHeaderFetch {
906-
t.Errorf("rollback head mismatch: have %v, want at most %v", head, MaxHeaderFetch)
907-
}
908-
// Attempt to sync with an attacker that feeds junk during the block import phase.
909-
// This should result in both the last fsHeaderSafetyNet number of headers being
910-
// rolled back, and also the pivot point being reverted to a non-block status.
911-
missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1
912-
913-
blockAttacker := tester.newPeer("block-attack", protocol, chain.blocks[1:])
914-
fastAttacker.withholdHeaders[chain.blocks[missing].Hash()] = struct{}{} // Make sure the fast-attacker doesn't fill in
915-
blockAttacker.withholdHeaders[chain.blocks[missing].Hash()] = struct{}{}
916-
917-
if err := tester.sync("block-attack", nil, mode); err == nil {
918-
t.Fatalf("succeeded block attacker synchronisation")
919-
}
920-
if head := tester.chain.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch {
921-
t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch)
922-
}
923-
if mode == SnapSync {
924-
if head := tester.chain.CurrentBlock().Number.Uint64(); head != 0 {
925-
t.Errorf("fast sync pivot block #%d not rolled back", head)
926-
}
927-
}
928-
// Attempt to sync with an attacker that withholds promised blocks after the
929-
// fast sync pivot point. This could be a trial to leave the node with a bad
930-
// but already imported pivot block.
931-
withholdAttacker := tester.newPeer("withhold-attack", protocol, chain.blocks[1:])
932-
933-
tester.downloader.syncInitHook = func(uint64, uint64) {
934-
for i := missing; i < len(chain.blocks); i++ {
935-
withholdAttacker.withholdHeaders[chain.blocks[i].Hash()] = struct{}{}
936-
}
937-
tester.downloader.syncInitHook = nil
938-
}
939-
if err := tester.sync("withhold-attack", nil, mode); err == nil {
940-
t.Fatalf("succeeded withholding attacker synchronisation")
941-
}
942-
if head := tester.chain.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch {
943-
t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch)
944-
}
945-
if mode == SnapSync {
946-
if head := tester.chain.CurrentBlock().Number.Uint64(); head != 0 {
947-
t.Errorf("fast sync pivot block #%d not rolled back", head)
948-
}
949-
}
950-
// Synchronise with the valid peer and make sure sync succeeds. Since the last rollback
951-
// should also disable fast syncing for this process, verify that we did a fresh full
952-
// sync. Note, we can't assert anything about the receipts since we won't purge the
953-
// database of them, hence we can't use assertOwnChain.
954-
tester.newPeer("valid", protocol, chain.blocks[1:])
955-
if err := tester.sync("valid", nil, mode); err != nil {
956-
t.Fatalf("failed to synchronise blocks: %v", err)
957-
}
958-
assertOwnChain(t, tester, len(chain.blocks))
959-
}
960-
961881
// Tests that a peer advertising a high TD doesn't get to stall the downloader
962882
// afterwards by not sending any useful hashes.
963883
func TestHighTDStarvationAttack66Full(t *testing.T) {

0 commit comments

Comments
 (0)