Patch for concurrent iterator & others (onto v1.11.6) #386
@ -237,6 +237,10 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
|||||||
log.Warn("Forkchoice requested unknown head", "hash", update.HeadBlockHash)
|
log.Warn("Forkchoice requested unknown head", "hash", update.HeadBlockHash)
|
||||||
return engine.STATUS_SYNCING, nil
|
return engine.STATUS_SYNCING, nil
|
||||||
}
|
}
|
||||||
|
// If the finalized hash is known, we can direct the downloader to move
|
||||||
|
// potentially more data to the freezer from the get go.
|
||||||
|
finalized := api.remoteBlocks.get(update.FinalizedBlockHash)
|
||||||
|
|
||||||
// Header advertised via a past newPayload request. Start syncing to it.
|
// Header advertised via a past newPayload request. Start syncing to it.
|
||||||
// Before we do however, make sure any legacy sync in switched off so we
|
// Before we do however, make sure any legacy sync in switched off so we
|
||||||
// don't accidentally have 2 cycles running.
|
// don't accidentally have 2 cycles running.
|
||||||
@ -244,8 +248,16 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
|||||||
merger.ReachTTD()
|
merger.ReachTTD()
|
||||||
api.eth.Downloader().Cancel()
|
api.eth.Downloader().Cancel()
|
||||||
}
|
}
|
||||||
log.Info("Forkchoice requested sync to new head", "number", header.Number, "hash", header.Hash())
|
context := []interface{}{"number", header.Number, "hash", header.Hash()}
|
||||||
if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), header); err != nil {
|
if update.FinalizedBlockHash != (common.Hash{}) {
|
||||||
|
if finalized == nil {
|
||||||
|
context = append(context, []interface{}{"finalized", "unknown"}...)
|
||||||
|
} else {
|
||||||
|
context = append(context, []interface{}{"finalized", finalized.Number}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info("Forkchoice requested sync to new head", context...)
|
||||||
|
if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), header, finalized); err != nil {
|
||||||
return engine.STATUS_SYNCING, err
|
return engine.STATUS_SYNCING, err
|
||||||
}
|
}
|
||||||
return engine.STATUS_SYNCING, nil
|
return engine.STATUS_SYNCING, nil
|
||||||
|
@ -31,9 +31,12 @@ import (
|
|||||||
const maxTrackedPayloads = 10
|
const maxTrackedPayloads = 10
|
||||||
|
|
||||||
// maxTrackedHeaders is the maximum number of executed payloads the execution
|
// maxTrackedHeaders is the maximum number of executed payloads the execution
|
||||||
// engine tracks before evicting old ones. Ideally we should only ever track the
|
// engine tracks before evicting old ones. These are tracked outside the chain
|
||||||
// latest one; but have a slight wiggle room for non-ideal conditions.
|
// during initial sync to allow ForkchoiceUpdate to reference past blocks via
|
||||||
const maxTrackedHeaders = 10
|
// hashes only. For the sync target it would be enough to track only the latest
|
||||||
|
// header, but snap sync also needs the latest finalized height for the ancient
|
||||||
|
// limit.
|
||||||
|
const maxTrackedHeaders = 96
|
||||||
|
|
||||||
// payloadQueueItem represents an id->payload tuple to store until it's retrieved
|
// payloadQueueItem represents an id->payload tuple to store until it's retrieved
|
||||||
// or evicted.
|
// or evicted.
|
||||||
|
@ -75,7 +75,7 @@ func (tester *FullSyncTester) Start() error {
|
|||||||
}
|
}
|
||||||
// Trigger beacon sync with the provided block header as
|
// Trigger beacon sync with the provided block header as
|
||||||
// trusted chain head.
|
// trusted chain head.
|
||||||
err := tester.api.eth.Downloader().BeaconSync(downloader.FullSync, tester.block.Header())
|
err := tester.api.eth.Downloader().BeaconSync(downloader.FullSync, tester.block.Header(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Failed to beacon sync", "err", err)
|
log.Info("Failed to beacon sync", "err", err)
|
||||||
}
|
}
|
||||||
|
@ -151,8 +151,8 @@ func (d *Downloader) SetBadBlockCallback(onBadBlock badBlockFn) {
|
|||||||
//
|
//
|
||||||
// Internally backfilling and state sync is done the same way, but the header
|
// Internally backfilling and state sync is done the same way, but the header
|
||||||
// retrieval and scheduling is replaced.
|
// retrieval and scheduling is replaced.
|
||||||
func (d *Downloader) BeaconSync(mode SyncMode, head *types.Header) error {
|
func (d *Downloader) BeaconSync(mode SyncMode, head *types.Header, final *types.Header) error {
|
||||||
return d.beaconSync(mode, head, true)
|
return d.beaconSync(mode, head, final, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeaconExtend is an optimistic version of BeaconSync, where an attempt is made
|
// BeaconExtend is an optimistic version of BeaconSync, where an attempt is made
|
||||||
@ -162,7 +162,7 @@ func (d *Downloader) BeaconSync(mode SyncMode, head *types.Header) error {
|
|||||||
// This is useful if a beacon client is feeding us large chunks of payloads to run,
|
// This is useful if a beacon client is feeding us large chunks of payloads to run,
|
||||||
// but is not setting the head after each.
|
// but is not setting the head after each.
|
||||||
func (d *Downloader) BeaconExtend(mode SyncMode, head *types.Header) error {
|
func (d *Downloader) BeaconExtend(mode SyncMode, head *types.Header) error {
|
||||||
return d.beaconSync(mode, head, false)
|
return d.beaconSync(mode, head, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// beaconSync is the post-merge version of the chain synchronization, where the
|
// beaconSync is the post-merge version of the chain synchronization, where the
|
||||||
@ -171,7 +171,7 @@ func (d *Downloader) BeaconExtend(mode SyncMode, head *types.Header) error {
|
|||||||
//
|
//
|
||||||
// Internally backfilling and state sync is done the same way, but the header
|
// Internally backfilling and state sync is done the same way, but the header
|
||||||
// retrieval and scheduling is replaced.
|
// retrieval and scheduling is replaced.
|
||||||
func (d *Downloader) beaconSync(mode SyncMode, head *types.Header, force bool) error {
|
func (d *Downloader) beaconSync(mode SyncMode, head *types.Header, final *types.Header, force bool) error {
|
||||||
// When the downloader starts a sync cycle, it needs to be aware of the sync
|
// When the downloader starts a sync cycle, it needs to be aware of the sync
|
||||||
// mode to use (full, snap). To keep the skeleton chain oblivious, inject the
|
// mode to use (full, snap). To keep the skeleton chain oblivious, inject the
|
||||||
// mode into the backfiller directly.
|
// mode into the backfiller directly.
|
||||||
@ -181,7 +181,7 @@ func (d *Downloader) beaconSync(mode SyncMode, head *types.Header, force bool) e
|
|||||||
d.skeleton.filler.(*beaconBackfiller).setMode(mode)
|
d.skeleton.filler.(*beaconBackfiller).setMode(mode)
|
||||||
|
|
||||||
// Signal the skeleton sync to switch to a new head, however it wants
|
// Signal the skeleton sync to switch to a new head, however it wants
|
||||||
if err := d.skeleton.Sync(head, force); err != nil {
|
if err := d.skeleton.Sync(head, final, force); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -207,7 +207,7 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) {
|
|||||||
number := chainHead.Number.Uint64()
|
number := chainHead.Number.Uint64()
|
||||||
|
|
||||||
// Retrieve the skeleton bounds and ensure they are linked to the local chain
|
// Retrieve the skeleton bounds and ensure they are linked to the local chain
|
||||||
beaconHead, beaconTail, err := d.skeleton.Bounds()
|
beaconHead, beaconTail, _, err := d.skeleton.Bounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a programming error. The chain backfiller was called with an
|
// This is a programming error. The chain backfiller was called with an
|
||||||
// invalid beacon sync state. Ideally we would panic here, but erroring
|
// invalid beacon sync state. Ideally we would panic here, but erroring
|
||||||
@ -272,7 +272,7 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) {
|
|||||||
// until sync errors or is finished.
|
// until sync errors or is finished.
|
||||||
func (d *Downloader) fetchBeaconHeaders(from uint64) error {
|
func (d *Downloader) fetchBeaconHeaders(from uint64) error {
|
||||||
var head *types.Header
|
var head *types.Header
|
||||||
_, tail, err := d.skeleton.Bounds()
|
_, tail, _, err := d.skeleton.Bounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -292,7 +292,7 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error {
|
|||||||
for {
|
for {
|
||||||
// Some beacon headers might have appeared since the last cycle, make
|
// Some beacon headers might have appeared since the last cycle, make
|
||||||
// sure we're always syncing to all available ones
|
// sure we're always syncing to all available ones
|
||||||
head, _, err = d.skeleton.Bounds()
|
head, _, _, err = d.skeleton.Bounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -480,7 +480,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
|
|||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
// Look up the sync boundaries: the common ancestor and the target block
|
// Look up the sync boundaries: the common ancestor and the target block
|
||||||
var latest, pivot *types.Header
|
var latest, pivot, final *types.Header
|
||||||
if !beaconMode {
|
if !beaconMode {
|
||||||
// In legacy mode, use the master peer to retrieve the headers from
|
// In legacy mode, use the master peer to retrieve the headers from
|
||||||
latest, pivot, err = d.fetchHead(p)
|
latest, pivot, err = d.fetchHead(p)
|
||||||
@ -489,7 +489,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// In beacon mode, use the skeleton chain to retrieve the headers from
|
// In beacon mode, use the skeleton chain to retrieve the headers from
|
||||||
latest, _, err = d.skeleton.Bounds()
|
latest, _, final, err = d.skeleton.Bounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -499,7 +499,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
|
|||||||
// Retrieve the pivot header from the skeleton chain segment but
|
// Retrieve the pivot header from the skeleton chain segment but
|
||||||
// fallback to local chain if it's not found in skeleton space.
|
// fallback to local chain if it's not found in skeleton space.
|
||||||
if pivot = d.skeleton.Header(number); pivot == nil {
|
if pivot = d.skeleton.Header(number); pivot == nil {
|
||||||
_, oldest, _ := d.skeleton.Bounds() // error is already checked
|
_, oldest, _, _ := d.skeleton.Bounds() // error is already checked
|
||||||
if number < oldest.Number.Uint64() {
|
if number < oldest.Number.Uint64() {
|
||||||
count := int(oldest.Number.Uint64() - number) // it's capped by fsMinFullBlocks
|
count := int(oldest.Number.Uint64() - number) // it's capped by fsMinFullBlocks
|
||||||
headers := d.readHeaderRange(oldest, count)
|
headers := d.readHeaderRange(oldest, count)
|
||||||
@ -567,26 +567,41 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
|
|||||||
d.committed = 0
|
d.committed = 0
|
||||||
}
|
}
|
||||||
if mode == SnapSync {
|
if mode == SnapSync {
|
||||||
// Set the ancient data limitation.
|
// Set the ancient data limitation. If we are running snap sync, all block
|
||||||
// If we are running snap sync, all block data older than ancientLimit will be
|
// data older than ancientLimit will be written to the ancient store. More
|
||||||
// written to the ancient store. More recent data will be written to the active
|
// recent data will be written to the active database and will wait for the
|
||||||
// database and will wait for the freezer to migrate.
|
// freezer to migrate.
|
||||||
//
|
//
|
||||||
// If there is a checkpoint available, then calculate the ancientLimit through
|
// If the network is post-merge, use either the last announced finalized
|
||||||
// that. Otherwise calculate the ancient limit through the advertised height
|
// block as the ancient limit, or if we haven't yet received one, the head-
|
||||||
// of the remote peer.
|
// a max fork ancestry limit. One quirky case if we've already passed the
|
||||||
|
// finalized block, in which case the skeleton.Bounds will return nil and
|
||||||
|
// we'll revert to head - 90K. That's fine, we're finishing sync anyway.
|
||||||
//
|
//
|
||||||
// The reason for picking checkpoint first is that a malicious peer can give us
|
// For non-merged networks, if there is a checkpoint available, then calculate
|
||||||
// a fake (very high) height, forcing the ancient limit to also be very high.
|
// the ancientLimit through that. Otherwise calculate the ancient limit through
|
||||||
// The peer would start to feed us valid blocks until head, resulting in all of
|
// the advertised height of the remote peer. This most is mostly a fallback for
|
||||||
// the blocks might be written into the ancient store. A following mini-reorg
|
// legacy networks, but should eventually be droppped. TODO(karalabe).
|
||||||
// could cause issues.
|
if beaconMode {
|
||||||
if d.checkpoint != 0 && d.checkpoint > fullMaxForkAncestry+1 {
|
// Beacon sync, use the latest finalized block as the ancient limit
|
||||||
d.ancientLimit = d.checkpoint
|
// or a reasonable height if no finalized block is yet announced.
|
||||||
} else if height > fullMaxForkAncestry+1 {
|
if final != nil {
|
||||||
d.ancientLimit = height - fullMaxForkAncestry - 1
|
d.ancientLimit = final.Number.Uint64()
|
||||||
|
} else if height > fullMaxForkAncestry+1 {
|
||||||
|
d.ancientLimit = height - fullMaxForkAncestry - 1
|
||||||
|
} else {
|
||||||
|
d.ancientLimit = 0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
d.ancientLimit = 0
|
// Legacy sync, use any hardcoded checkpoints or the best announcement
|
||||||
|
// we have from the remote peer. TODO(karalabe): Drop this pathway.
|
||||||
|
if d.checkpoint != 0 && d.checkpoint > fullMaxForkAncestry+1 {
|
||||||
|
d.ancientLimit = d.checkpoint
|
||||||
|
} else if height > fullMaxForkAncestry+1 {
|
||||||
|
d.ancientLimit = height - fullMaxForkAncestry - 1
|
||||||
|
} else {
|
||||||
|
d.ancientLimit = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
frozen, _ := d.stateDB.Ancients() // Ignore the error here since light client can also hit here.
|
frozen, _ := d.stateDB.Ancients() // Ignore the error here since light client can also hit here.
|
||||||
|
|
||||||
@ -1566,7 +1581,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
|
|||||||
|
|
||||||
// In post-merge, notify the engine API of encountered bad chains
|
// In post-merge, notify the engine API of encountered bad chains
|
||||||
if d.badBlock != nil {
|
if d.badBlock != nil {
|
||||||
head, _, err := d.skeleton.Bounds()
|
head, _, _, err := d.skeleton.Bounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to retrieve beacon bounds for bad block reporting", "err", err)
|
log.Error("Failed to retrieve beacon bounds for bad block reporting", "err", err)
|
||||||
} else {
|
} else {
|
||||||
@ -1860,7 +1875,7 @@ func (d *Downloader) reportSnapSyncProgress(force bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Retrieve the current chain head and calculate the ETA
|
// Retrieve the current chain head and calculate the ETA
|
||||||
latest, _, err := d.skeleton.Bounds()
|
latest, _, _, err := d.skeleton.Bounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// We're going to cheat for non-merged networks, but that's fine
|
// We're going to cheat for non-merged networks, but that's fine
|
||||||
latest = d.pivotHeader
|
latest = d.pivotHeader
|
||||||
|
@ -1478,7 +1478,7 @@ func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) {
|
|||||||
if c.local > 0 {
|
if c.local > 0 {
|
||||||
tester.chain.InsertChain(chain.blocks[1 : c.local+1])
|
tester.chain.InsertChain(chain.blocks[1 : c.local+1])
|
||||||
}
|
}
|
||||||
if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header()); err != nil {
|
if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil {
|
||||||
t.Fatalf("Failed to beacon sync chain %v %v", c.name, err)
|
t.Fatalf("Failed to beacon sync chain %v %v", c.name, err)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
@ -102,6 +102,7 @@ type subchain struct {
|
|||||||
// suspended skeleton sync without prior knowledge of all prior suspension points.
|
// suspended skeleton sync without prior knowledge of all prior suspension points.
|
||||||
type skeletonProgress struct {
|
type skeletonProgress struct {
|
||||||
Subchains []*subchain // Disjoint subchains downloaded until now
|
Subchains []*subchain // Disjoint subchains downloaded until now
|
||||||
|
Finalized *uint64 // Last known finalized block number
|
||||||
}
|
}
|
||||||
|
|
||||||
// headUpdate is a notification that the beacon sync should switch to a new target.
|
// headUpdate is a notification that the beacon sync should switch to a new target.
|
||||||
@ -109,6 +110,7 @@ type skeletonProgress struct {
|
|||||||
// extend it and fail if it's not possible.
|
// extend it and fail if it's not possible.
|
||||||
type headUpdate struct {
|
type headUpdate struct {
|
||||||
header *types.Header // Header to update the sync target to
|
header *types.Header // Header to update the sync target to
|
||||||
|
final *types.Header // Finalized header to use as thresholds
|
||||||
force bool // Whether to force the update or only extend if possible
|
force bool // Whether to force the update or only extend if possible
|
||||||
errc chan error // Channel to signal acceptance of the new head
|
errc chan error // Channel to signal acceptance of the new head
|
||||||
}
|
}
|
||||||
@ -321,12 +323,12 @@ func (s *skeleton) Terminate() error {
|
|||||||
//
|
//
|
||||||
// This method does not block, rather it just waits until the syncer receives the
|
// This method does not block, rather it just waits until the syncer receives the
|
||||||
// fed header. What the syncer does with it is the syncer's problem.
|
// fed header. What the syncer does with it is the syncer's problem.
|
||||||
func (s *skeleton) Sync(head *types.Header, force bool) error {
|
func (s *skeleton) Sync(head *types.Header, final *types.Header, force bool) error {
|
||||||
log.Trace("New skeleton head announced", "number", head.Number, "hash", head.Hash(), "force", force)
|
log.Trace("New skeleton head announced", "number", head.Number, "hash", head.Hash(), "force", force)
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case s.headEvents <- &headUpdate{header: head, force: force, errc: errc}:
|
case s.headEvents <- &headUpdate{header: head, final: final, force: force, errc: errc}:
|
||||||
return <-errc
|
return <-errc
|
||||||
case <-s.terminated:
|
case <-s.terminated:
|
||||||
return errTerminated
|
return errTerminated
|
||||||
@ -437,7 +439,7 @@ func (s *skeleton) sync(head *types.Header) (*types.Header, error) {
|
|||||||
// we don't seamlessly integrate reorgs to keep things simple. If the
|
// we don't seamlessly integrate reorgs to keep things simple. If the
|
||||||
// network starts doing many mini reorgs, it might be worthwhile handling
|
// network starts doing many mini reorgs, it might be worthwhile handling
|
||||||
// a limited depth without an error.
|
// a limited depth without an error.
|
||||||
if reorged := s.processNewHead(event.header, event.force); reorged {
|
if reorged := s.processNewHead(event.header, event.final, event.force); reorged {
|
||||||
// If a reorg is needed, and we're forcing the new head, signal
|
// If a reorg is needed, and we're forcing the new head, signal
|
||||||
// the syncer to tear down and start over. Otherwise, drop the
|
// the syncer to tear down and start over. Otherwise, drop the
|
||||||
// non-force reorg.
|
// non-force reorg.
|
||||||
@ -590,7 +592,17 @@ func (s *skeleton) saveSyncStatus(db ethdb.KeyValueWriter) {
|
|||||||
// accepts and integrates it into the skeleton or requests a reorg. Upon reorg,
|
// accepts and integrates it into the skeleton or requests a reorg. Upon reorg,
|
||||||
// the syncer will tear itself down and restart with a fresh head. It is simpler
|
// the syncer will tear itself down and restart with a fresh head. It is simpler
|
||||||
// to reconstruct the sync state than to mutate it and hope for the best.
|
// to reconstruct the sync state than to mutate it and hope for the best.
|
||||||
func (s *skeleton) processNewHead(head *types.Header, force bool) bool {
|
func (s *skeleton) processNewHead(head *types.Header, final *types.Header, force bool) bool {
|
||||||
|
// If a new finalized block was announced, update the sync process independent
|
||||||
|
// of what happens with the sync head below
|
||||||
|
if final != nil {
|
||||||
|
if number := final.Number.Uint64(); s.progress.Finalized == nil || *s.progress.Finalized != number {
|
||||||
|
s.progress.Finalized = new(uint64)
|
||||||
|
*s.progress.Finalized = final.Number.Uint64()
|
||||||
|
|
||||||
|
s.saveSyncStatus(s.db)
|
||||||
|
}
|
||||||
|
}
|
||||||
// If the header cannot be inserted without interruption, return an error for
|
// If the header cannot be inserted without interruption, return an error for
|
||||||
// the outer loop to tear down the skeleton sync and restart it
|
// the outer loop to tear down the skeleton sync and restart it
|
||||||
number := head.Number.Uint64()
|
number := head.Number.Uint64()
|
||||||
@ -1150,9 +1162,10 @@ func (s *skeleton) cleanStales(filled *types.Header) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bounds retrieves the current head and tail tracked by the skeleton syncer.
|
// Bounds retrieves the current head and tail tracked by the skeleton syncer
|
||||||
// This method is used by the backfiller, whose life cycle is controlled by the
|
// and optionally the last known finalized header if any was announced and if
|
||||||
// skeleton syncer.
|
// it is still in the sync range. This method is used by the backfiller, whose
|
||||||
|
// life cycle is controlled by the skeleton syncer.
|
||||||
//
|
//
|
||||||
// Note, the method will not use the internal state of the skeleton, but will
|
// Note, the method will not use the internal state of the skeleton, but will
|
||||||
// rather blindly pull stuff from the database. This is fine, because the back-
|
// rather blindly pull stuff from the database. This is fine, because the back-
|
||||||
@ -1160,28 +1173,34 @@ func (s *skeleton) cleanStales(filled *types.Header) error {
|
|||||||
// There might be new heads appended, but those are atomic from the perspective
|
// There might be new heads appended, but those are atomic from the perspective
|
||||||
// of this method. Any head reorg will first tear down the backfiller and only
|
// of this method. Any head reorg will first tear down the backfiller and only
|
||||||
// then make the modification.
|
// then make the modification.
|
||||||
func (s *skeleton) Bounds() (head *types.Header, tail *types.Header, err error) {
|
func (s *skeleton) Bounds() (head *types.Header, tail *types.Header, final *types.Header, err error) {
|
||||||
// Read the current sync progress from disk and figure out the current head.
|
// Read the current sync progress from disk and figure out the current head.
|
||||||
// Although there's a lot of error handling here, these are mostly as sanity
|
// Although there's a lot of error handling here, these are mostly as sanity
|
||||||
// checks to avoid crashing if a programming error happens. These should not
|
// checks to avoid crashing if a programming error happens. These should not
|
||||||
// happen in live code.
|
// happen in live code.
|
||||||
status := rawdb.ReadSkeletonSyncStatus(s.db)
|
status := rawdb.ReadSkeletonSyncStatus(s.db)
|
||||||
if len(status) == 0 {
|
if len(status) == 0 {
|
||||||
return nil, nil, errors.New("beacon sync not yet started")
|
return nil, nil, nil, errors.New("beacon sync not yet started")
|
||||||
}
|
}
|
||||||
progress := new(skeletonProgress)
|
progress := new(skeletonProgress)
|
||||||
if err := json.Unmarshal(status, progress); err != nil {
|
if err := json.Unmarshal(status, progress); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
head = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Head)
|
head = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Head)
|
||||||
if head == nil {
|
if head == nil {
|
||||||
return nil, nil, fmt.Errorf("head skeleton header %d is missing", progress.Subchains[0].Head)
|
return nil, nil, nil, fmt.Errorf("head skeleton header %d is missing", progress.Subchains[0].Head)
|
||||||
}
|
}
|
||||||
tail = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Tail)
|
tail = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Tail)
|
||||||
if tail == nil {
|
if tail == nil {
|
||||||
return nil, nil, fmt.Errorf("tail skeleton header %d is missing", progress.Subchains[0].Tail)
|
return nil, nil, nil, fmt.Errorf("tail skeleton header %d is missing", progress.Subchains[0].Tail)
|
||||||
}
|
}
|
||||||
return head, tail, nil
|
if progress.Finalized != nil && tail.Number.Uint64() <= *progress.Finalized && *progress.Finalized <= head.Number.Uint64() {
|
||||||
|
final = rawdb.ReadSkeletonHeader(s.db, *progress.Finalized)
|
||||||
|
if final == nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("finalized skeleton header %d is missing", *progress.Finalized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return head, tail, final, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header retrieves a specific header tracked by the skeleton syncer. This method
|
// Header retrieves a specific header tracked by the skeleton syncer. This method
|
||||||
|
@ -370,7 +370,7 @@ func TestSkeletonSyncInit(t *testing.T) {
|
|||||||
|
|
||||||
skeleton := newSkeleton(db, newPeerSet(), nil, newHookedBackfiller())
|
skeleton := newSkeleton(db, newPeerSet(), nil, newHookedBackfiller())
|
||||||
skeleton.syncStarting = func() { close(wait) }
|
skeleton.syncStarting = func() { close(wait) }
|
||||||
skeleton.Sync(tt.head, true)
|
skeleton.Sync(tt.head, nil, true)
|
||||||
|
|
||||||
<-wait
|
<-wait
|
||||||
skeleton.Terminate()
|
skeleton.Terminate()
|
||||||
@ -484,10 +484,10 @@ func TestSkeletonSyncExtend(t *testing.T) {
|
|||||||
|
|
||||||
skeleton := newSkeleton(db, newPeerSet(), nil, newHookedBackfiller())
|
skeleton := newSkeleton(db, newPeerSet(), nil, newHookedBackfiller())
|
||||||
skeleton.syncStarting = func() { close(wait) }
|
skeleton.syncStarting = func() { close(wait) }
|
||||||
skeleton.Sync(tt.head, true)
|
skeleton.Sync(tt.head, nil, true)
|
||||||
|
|
||||||
<-wait
|
<-wait
|
||||||
if err := skeleton.Sync(tt.extend, false); err != tt.err {
|
if err := skeleton.Sync(tt.extend, nil, false); err != tt.err {
|
||||||
t.Errorf("test %d: extension failure mismatch: have %v, want %v", i, err, tt.err)
|
t.Errorf("test %d: extension failure mismatch: have %v, want %v", i, err, tt.err)
|
||||||
}
|
}
|
||||||
skeleton.Terminate()
|
skeleton.Terminate()
|
||||||
@ -859,7 +859,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// Create a skeleton sync and run a cycle
|
// Create a skeleton sync and run a cycle
|
||||||
skeleton := newSkeleton(db, peerset, drop, filler)
|
skeleton := newSkeleton(db, peerset, drop, filler)
|
||||||
skeleton.Sync(tt.head, true)
|
skeleton.Sync(tt.head, nil, true)
|
||||||
|
|
||||||
var progress skeletonProgress
|
var progress skeletonProgress
|
||||||
// Wait a bit (bleah) for the initial sync loop to go to idle. This might
|
// Wait a bit (bleah) for the initial sync loop to go to idle. This might
|
||||||
@ -910,7 +910,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// Apply the post-init events if there's any
|
// Apply the post-init events if there's any
|
||||||
if tt.newHead != nil {
|
if tt.newHead != nil {
|
||||||
skeleton.Sync(tt.newHead, true)
|
skeleton.Sync(tt.newHead, nil, true)
|
||||||
}
|
}
|
||||||
if tt.newPeer != nil {
|
if tt.newPeer != nil {
|
||||||
if err := peerset.Register(newPeerConnection(tt.newPeer.id, eth.ETH66, tt.newPeer, log.New("id", tt.newPeer.id))); err != nil {
|
if err := peerset.Register(newPeerConnection(tt.newPeer.id, eth.ETH66, tt.newPeer, log.New("id", tt.newPeer.id))); err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user