Skip to content

Commit 28ec260

Browse files
authored
eth/downloader: retrieve pivot header from local chain if necessary (#24610)
* eth/downloader: retrieve pivot header from local chain if necessary * eth/downloader: improve readability * eth/downloader: update fix * eth/downloader: add beacon sync tests * eth/downloader: remove duplicated code
1 parent 1e973a9 commit 28ec260

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed

eth/downloader/beaconsync.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,11 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error {
265265
hashes = make([]common.Hash, 0, maxHeadersProcess)
266266
)
267267
for i := 0; i < maxHeadersProcess && from <= head.Number.Uint64(); i++ {
268-
headers = append(headers, d.skeleton.Header(from))
268+
header := d.skeleton.Header(from)
269+
if header == nil {
270+
header = d.lightchain.GetHeaderByNumber(from)
271+
}
272+
headers = append(headers, header)
269273
hashes = append(hashes, headers[i].Hash())
270274
from++
271275
}

eth/downloader/downloader.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ var (
7878
errCanceled = errors.New("syncing canceled (requested)")
7979
errTooOld = errors.New("peer's protocol version too old")
8080
errNoAncestorFound = errors.New("no common ancestor found")
81+
errNoPivotHeader = errors.New("pivot header is not found")
8182
ErrMergeTransition = errors.New("legacy sync reached the merge")
8283
)
8384

@@ -158,6 +159,9 @@ type LightChain interface {
158159
// GetHeaderByHash retrieves a header from the local chain.
159160
GetHeaderByHash(common.Hash) *types.Header
160161

162+
// GetHeaderByNumber retrieves a block header from the local chain by number.
163+
GetHeaderByNumber(number uint64) *types.Header
164+
161165
// CurrentHeader retrieves the head header from the local chain.
162166
CurrentHeader() *types.Header
163167

@@ -477,7 +481,20 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
477481
return err
478482
}
479483
if latest.Number.Uint64() > uint64(fsMinFullBlocks) {
480-
pivot = d.skeleton.Header(latest.Number.Uint64() - uint64(fsMinFullBlocks))
484+
number := latest.Number.Uint64() - uint64(fsMinFullBlocks)
485+
486+
// Retrieve the pivot header from the skeleton chain segment but
487+
// fallback to local chain if it's not found in skeleton space.
488+
if pivot = d.skeleton.Header(number); pivot == nil {
489+
pivot = d.lightchain.GetHeaderByNumber(number)
490+
}
491+
// Print an error log and return directly in case the pivot header
492+
// is still not found. It means the skeleton chain is not linked
493+
// correctly with local chain.
494+
if pivot == nil {
495+
log.Error("Pivot header is not found", "number", number)
496+
return errNoPivotHeader
497+
}
481498
}
482499
}
483500
// If no pivot block was returned, the head is below the min full block

eth/downloader/downloader_test.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ type downloadTester struct {
5656

5757
// newTester creates a new downloader test mocker.
5858
func newTester() *downloadTester {
59+
return newTesterWithNotification(nil)
60+
}
61+
62+
// newTester creates a new downloader test mocker.
63+
func newTesterWithNotification(success func()) *downloadTester {
5964
freezer, err := ioutil.TempDir("", "")
6065
if err != nil {
6166
panic(err)
@@ -75,7 +80,7 @@ func newTester() *downloadTester {
7580
chain: chain,
7681
peers: make(map[string]*downloadTesterPeer),
7782
}
78-
tester.downloader = New(0, db, new(event.TypeMux), tester.chain, nil, tester.dropPeer, nil)
83+
tester.downloader = New(0, db, new(event.TypeMux), tester.chain, nil, tester.dropPeer, success)
7984
return tester
8085
}
8186

@@ -1368,3 +1373,51 @@ func testCheckpointEnforcement(t *testing.T, protocol uint, mode SyncMode) {
13681373
assertOwnChain(t, tester, len(chain.blocks))
13691374
}
13701375
}
1376+
1377+
// Tests that peers below a pre-configured checkpoint block are prevented from
1378+
// being fast-synced from, avoiding potential cheap eclipse attacks.
1379+
func TestBeaconSync66Full(t *testing.T) { testBeaconSync(t, eth.ETH66, FullSync) }
1380+
func TestBeaconSync66Snap(t *testing.T) { testBeaconSync(t, eth.ETH66, SnapSync) }
1381+
1382+
func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) {
1383+
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
1384+
1385+
var cases = []struct {
1386+
name string // The name of testing scenario
1387+
local int // The length of local chain(canonical chain assumed), 0 means genesis is the head
1388+
}{
1389+
{name: "Beacon sync since genesis", local: 0},
1390+
{name: "Beacon sync with short local chain", local: 1},
1391+
{name: "Beacon sync with long local chain", local: blockCacheMaxItems - 15 - fsMinFullBlocks/2},
1392+
{name: "Beacon sync with full local chain", local: blockCacheMaxItems - 15 - 1},
1393+
}
1394+
for _, c := range cases {
1395+
t.Run(c.name, func(t *testing.T) {
1396+
success := make(chan struct{})
1397+
tester := newTesterWithNotification(func() {
1398+
close(success)
1399+
})
1400+
defer tester.terminate()
1401+
1402+
chain := testChainBase.shorten(blockCacheMaxItems - 15)
1403+
tester.newPeer("peer", protocol, chain.blocks[1:])
1404+
1405+
// Build the local chain segment if it's required
1406+
if c.local > 0 {
1407+
tester.chain.InsertChain(chain.blocks[1 : c.local+1])
1408+
}
1409+
if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header()); err != nil {
1410+
t.Fatalf("Failed to beacon sync chain %v %v", c.name, err)
1411+
}
1412+
select {
1413+
case <-success:
1414+
// Ok, downloader fully cancelled after sync cycle
1415+
if bs := int(tester.chain.CurrentBlock().NumberU64()) + 1; bs != len(chain.blocks) {
1416+
t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, len(chain.blocks))
1417+
}
1418+
case <-time.NewTimer(time.Second * 3).C:
1419+
t.Fatalf("Failed to sync chain in three seconds")
1420+
}
1421+
})
1422+
}
1423+
}

0 commit comments

Comments
 (0)