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: resolve local header by hash for beacon sync #24691

Merged
merged 5 commits into from
Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
27 changes: 25 additions & 2 deletions eth/downloader/beaconsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,23 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) {
// fetchBeaconHeaders feeds skeleton headers to the downloader queue for scheduling
// until sync errors or is finished.
func (d *Downloader) fetchBeaconHeaders(from uint64) error {
head, _, err := d.skeleton.Bounds()
head, tail, err := d.skeleton.Bounds()
if err != nil {
return err
}
// A part of headers are not in the skeleton space, try to resolve
// them from the local chain. Note the range should be very short
// and it should only happen when there are less than 64 post-merge
// blocks in the network.
var localHeaders []*types.Header
if from < tail.Number.Uint64() {
count := tail.Number.Uint64() - from
if count > uint64(fsMinFullBlocks) {
return fmt.Errorf("invalid origin (%d) of beacon sync (%d)", from, tail.Number)
}
localHeaders = d.readHeaderRange(tail, int(count))
log.Warn("Retrieved beacon headers from local", "from", from, "count", count)
}
for {
// Retrieve a batch of headers and feed it to the header processor
var (
Expand All @@ -266,8 +279,18 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error {
)
for i := 0; i < maxHeadersProcess && from <= head.Number.Uint64(); i++ {
header := d.skeleton.Header(from)

// The header is not found in skeleton space, try to find it in local chain.
if header == nil && from < tail.Number.Uint64() {
dist := tail.Number.Uint64() - from
if len(localHeaders) >= int(dist) {
header = localHeaders[dist-1]
Copy link
Member Author

Choose a reason for hiding this comment

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

In theory, we should check the number of retrieved pivot header. But I think it should always be correct unless we have the programming fault.

}
}
// The header is still missing, the beacon sync is corrupted and bail out
// the error here.
if header == nil {
header = d.lightchain.GetHeaderByNumber(from)
return fmt.Errorf("missing beacon header %d", from)
}
headers = append(headers, header)
hashes = append(hashes, headers[i].Hash())
Expand Down
35 changes: 31 additions & 4 deletions eth/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,6 @@ 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 @@ -486,7 +483,15 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
// Retrieve the pivot header from the skeleton chain segment but
// fallback to local chain if it's not found in skeleton space.
if pivot = d.skeleton.Header(number); pivot == nil {
pivot = d.lightchain.GetHeaderByNumber(number)
_, oldest, _ := d.skeleton.Bounds() // error is already checked
if number < oldest.Number.Uint64() {
count := int(oldest.Number.Uint64() - number) // it's capped by fsMinFullBlocks
headers := d.readHeaderRange(oldest, count)
if len(headers) == count {
pivot = headers[len(headers)-1]
Copy link
Member Author

Choose a reason for hiding this comment

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

In theory, we should check the number of retrieved pivot header. But I think it should always be correct unless we have the programming fault.

log.Warn("Retrieved pivot header from local", "number", pivot.Number, "hash", pivot.Hash(), "latest", latest.Number, "oldest", oldest.Number)
}
}
}
// Print an error log and return directly in case the pivot header
// is still not found. It means the skeleton chain is not linked
Expand Down Expand Up @@ -1772,3 +1777,25 @@ func (d *Downloader) DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) erro
return fmt.Errorf("unexpected snap packet type: %T", packet)
}
}

// readHeaderRange returns a list of headers, using the given last header as the base,
// and going backwards towards genesis. This method assumes that the caller already has
// placed a reasonable cap on count.
func (d *Downloader) readHeaderRange(last *types.Header, count int) []*types.Header {
var (
current = last
headers []*types.Header
)
for {
parent := d.lightchain.GetHeaderByHash(current.ParentHash)
if parent == nil {
break // The chain is not continuous, or the chain is exhausted
}
headers = append(headers, parent)
if len(headers) >= count {
break
}
current = parent
}
return headers
}