Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eth/downloader: retrieve pivot header from local chain if necessary #24610

Merged
merged 5 commits into from
Apr 4, 2022
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions eth/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ type LightChain interface {
// GetHeaderByHash retrieves a header from the local chain.
GetHeaderByHash(common.Hash) *types.Header

// GetHeaderByNumber retrieves a block header from the local chain by number.
GetHeaderByNumber(number uint64) *types.Header

// CurrentHeader retrieves the head header from the local chain.
CurrentHeader() *types.Header

Expand Down Expand Up @@ -477,15 +480,22 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
return err
}
if latest.Number.Uint64() > uint64(fsMinFullBlocks) {
pivot = d.skeleton.Header(latest.Number.Uint64() - uint64(fsMinFullBlocks))
number := latest.Number.Uint64() - uint64(fsMinFullBlocks)
pivot = d.skeleton.Header(number)
if pivot == nil {
rjl493456442 marked this conversation as resolved.
Show resolved Hide resolved
// Retrieve the pivot point from the local chain if it's
// not in the range of skeleton.
pivot = d.lightchain.GetHeaderByNumber(number)
}
}
}
// If no pivot block was returned, the head is below the min full block
// threshold (i.e. new chain). In that case we won't really snap sync
// anyway, but still need a valid pivot block to avoid some code hitting
// nil panics on access.
var syncState = true // means whether state sync is required in this cycle
if mode == SnapSync && pivot == nil {
pivot = d.blockchain.CurrentBlock().Header()
pivot, syncState = d.blockchain.CurrentBlock().Header(), false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code path is interesting. Previously the pivot block was retrieved from the master peer, so we had some fairly strong guarantees that it either exists or if it's nil, the head is < 64 depth. In that case, the original code is correct.

With the beacon sync addition, a nil pivot would mean that there's a gap between the downloaded skeleton chain segment and the local chain. But the skeleton syncer will only stop when the two are linked, which would mean that a gap is invalid internal state. In that case, my 2c is not to fix it, rather to return a sync error.

I could imagine that happening if there's some rollback raicing with a sync trigger. Or maybe sethead or something. Either way, what I'd do is to add the nil check after

				// Retrieve the pivot point from the local chain if it's
				// not in the range of skeleton.
				pivot = d.lightchain.GetHeaderByNumber(number)

and log an error and return. That IMHO is fine because it will just cause the local node to wait for the next block from the beacon client and then restart sync, filling any gas produced by some weird mechanism. It also makes the remainder of the code more deterministic vs. trying to correctly sync on top of an invalid internal state.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I updated it and also catch another issue.

}
height := latest.Number.Uint64()

Expand Down Expand Up @@ -516,7 +526,9 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
origin = 0
} else {
pivotNumber := pivot.Number.Uint64()
if pivotNumber <= origin {

// Cap the sync origin by pivot header when state sync is required.
if pivotNumber <= origin && syncState {
origin = pivotNumber - 1
}
// Write out the pivot into the database so a rollback beyond it will
Expand Down