Skip to content

Commit 5f3c58f

Browse files
jwasingerkaralabe
andauthored
eth/downloader: fix case where skeleton reorgs below the filled block (#29358)
This change adds a testcase and fixes a corner-case in the skeleton sync. With this change, when doing the skeleton cleanup, we check if the filled header is acually within the range of what we were meant to backfill. If not, it means the backfill was a noop (possibly because we started and stopped it so quickly that it didn't have time to do any meaningful work). In that case, just don't clean up anything. --------- Co-authored-by: Péter Szilágyi <peterke@gmail.com>
1 parent ade7515 commit 5f3c58f

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

eth/downloader/downloader_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,3 +1311,84 @@ func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) {
13111311
})
13121312
}
13131313
}
1314+
1315+
// Tests that synchronisation progress (origin block number and highest block
1316+
// number) is tracked and updated correctly in case of manual head reversion
1317+
func TestBeaconForkedSyncProgress68Full(t *testing.T) {
1318+
testBeaconForkedSyncProgress(t, eth.ETH68, FullSync)
1319+
}
1320+
func TestBeaconForkedSyncProgress68Snap(t *testing.T) {
1321+
testBeaconForkedSyncProgress(t, eth.ETH68, SnapSync)
1322+
}
1323+
func TestBeaconForkedSyncProgress68Light(t *testing.T) {
1324+
testBeaconForkedSyncProgress(t, eth.ETH68, LightSync)
1325+
}
1326+
1327+
func testBeaconForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
1328+
success := make(chan struct{})
1329+
tester := newTesterWithNotification(t, func() {
1330+
success <- struct{}{}
1331+
})
1332+
defer tester.terminate()
1333+
1334+
chainA := testChainForkLightA.shorten(len(testChainBase.blocks) + MaxHeaderFetch)
1335+
chainB := testChainForkLightB.shorten(len(testChainBase.blocks) + MaxHeaderFetch)
1336+
1337+
// Set a sync init hook to catch progress changes
1338+
starting := make(chan struct{})
1339+
progress := make(chan struct{})
1340+
1341+
tester.downloader.syncInitHook = func(origin, latest uint64) {
1342+
starting <- struct{}{}
1343+
<-progress
1344+
}
1345+
checkProgress(t, tester.downloader, "pristine", ethereum.SyncProgress{})
1346+
1347+
// Synchronise with one of the forks and check progress
1348+
tester.newPeer("fork A", protocol, chainA.blocks[1:])
1349+
pending := new(sync.WaitGroup)
1350+
pending.Add(1)
1351+
go func() {
1352+
defer pending.Done()
1353+
if err := tester.downloader.BeaconSync(mode, chainA.blocks[len(chainA.blocks)-1].Header(), nil); err != nil {
1354+
panic(fmt.Sprintf("failed to beacon sync: %v", err))
1355+
}
1356+
}()
1357+
1358+
<-starting
1359+
progress <- struct{}{}
1360+
select {
1361+
case <-success:
1362+
checkProgress(t, tester.downloader, "initial", ethereum.SyncProgress{
1363+
HighestBlock: uint64(len(chainA.blocks) - 1),
1364+
CurrentBlock: uint64(len(chainA.blocks) - 1),
1365+
})
1366+
case <-time.NewTimer(time.Second * 3).C:
1367+
t.Fatalf("Failed to sync chain in three seconds")
1368+
}
1369+
1370+
// Set the head to a second fork
1371+
tester.newPeer("fork B", protocol, chainB.blocks[1:])
1372+
pending.Add(1)
1373+
go func() {
1374+
defer pending.Done()
1375+
if err := tester.downloader.BeaconSync(mode, chainB.blocks[len(chainB.blocks)-1].Header(), nil); err != nil {
1376+
panic(fmt.Sprintf("failed to beacon sync: %v", err))
1377+
}
1378+
}()
1379+
1380+
<-starting
1381+
progress <- struct{}{}
1382+
1383+
// reorg below available state causes the state sync to rewind to genesis
1384+
select {
1385+
case <-success:
1386+
checkProgress(t, tester.downloader, "initial", ethereum.SyncProgress{
1387+
HighestBlock: uint64(len(chainB.blocks) - 1),
1388+
CurrentBlock: uint64(len(chainB.blocks) - 1),
1389+
StartingBlock: 0,
1390+
})
1391+
case <-time.NewTimer(time.Second * 3).C:
1392+
t.Fatalf("Failed to sync chain in three seconds")
1393+
}
1394+
}

eth/downloader/skeleton.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,16 @@ func (s *skeleton) cleanStales(filled *types.Header) error {
11321132
if number+1 == s.progress.Subchains[0].Tail {
11331133
return nil
11341134
}
1135+
// If the latest fill was on a different subchain, it means the backfiller
1136+
// was interrupted before it got to do any meaningful work, no cleanup
1137+
header := rawdb.ReadSkeletonHeader(s.db, filled.Number.Uint64())
1138+
if header == nil {
1139+
log.Debug("Filled header outside of skeleton range", "number", number, "head", s.progress.Subchains[0].Head, "tail", s.progress.Subchains[0].Tail)
1140+
return nil
1141+
} else if header.Hash() != filled.Hash() {
1142+
log.Debug("Filled header on different sidechain", "number", number, "filled", filled.Hash(), "skeleton", header.Hash())
1143+
return nil
1144+
}
11351145
var (
11361146
start uint64
11371147
end uint64

0 commit comments

Comments
 (0)