@@ -102,13 +102,15 @@ type subchain struct {
102102// suspended skeleton sync without prior knowledge of all prior suspension points.
103103type skeletonProgress struct {
104104 Subchains []* subchain // Disjoint subchains downloaded until now
105+ Finalized * uint64 // Last known finalized block number
105106}
106107
107108// headUpdate is a notification that the beacon sync should switch to a new target.
108109// The update might request whether to forcefully change the target, or only try to
109110// extend it and fail if it's not possible.
110111type headUpdate struct {
111112 header * types.Header // Header to update the sync target to
113+ final * types.Header // Finalized header to use as thresholds
112114 force bool // Whether to force the update or only extend if possible
113115 errc chan error // Channel to signal acceptance of the new head
114116}
@@ -321,12 +323,12 @@ func (s *skeleton) Terminate() error {
321323//
322324// This method does not block, rather it just waits until the syncer receives the
323325// fed header. What the syncer does with it is the syncer's problem.
324- func (s * skeleton ) Sync (head * types.Header , force bool ) error {
326+ func (s * skeleton ) Sync (head * types.Header , final * types. Header , force bool ) error {
325327 log .Trace ("New skeleton head announced" , "number" , head .Number , "hash" , head .Hash (), "force" , force )
326328 errc := make (chan error )
327329
328330 select {
329- case s .headEvents <- & headUpdate {header : head , force : force , errc : errc }:
331+ case s .headEvents <- & headUpdate {header : head , final : final , force : force , errc : errc }:
330332 return <- errc
331333 case <- s .terminated :
332334 return errTerminated
@@ -437,7 +439,7 @@ func (s *skeleton) sync(head *types.Header) (*types.Header, error) {
437439 // we don't seamlessly integrate reorgs to keep things simple. If the
438440 // network starts doing many mini reorgs, it might be worthwhile handling
439441 // a limited depth without an error.
440- if reorged := s .processNewHead (event .header , event .force ); reorged {
442+ if reorged := s .processNewHead (event .header , event .final , event . force ); reorged {
441443 // If a reorg is needed, and we're forcing the new head, signal
442444 // the syncer to tear down and start over. Otherwise, drop the
443445 // non-force reorg.
@@ -590,7 +592,17 @@ func (s *skeleton) saveSyncStatus(db ethdb.KeyValueWriter) {
590592// accepts and integrates it into the skeleton or requests a reorg. Upon reorg,
591593// the syncer will tear itself down and restart with a fresh head. It is simpler
592594// to reconstruct the sync state than to mutate it and hope for the best.
593- func (s * skeleton ) processNewHead (head * types.Header , force bool ) bool {
595+ func (s * skeleton ) processNewHead (head * types.Header , final * types.Header , force bool ) bool {
596+ // If a new finalized block was announced, update the sync process independent
597+ // of what happens with the sync head below
598+ if final != nil {
599+ if number := final .Number .Uint64 (); s .progress .Finalized == nil || * s .progress .Finalized != number {
600+ s .progress .Finalized = new (uint64 )
601+ * s .progress .Finalized = final .Number .Uint64 ()
602+
603+ s .saveSyncStatus (s .db )
604+ }
605+ }
594606 // If the header cannot be inserted without interruption, return an error for
595607 // the outer loop to tear down the skeleton sync and restart it
596608 number := head .Number .Uint64 ()
@@ -1150,38 +1162,45 @@ func (s *skeleton) cleanStales(filled *types.Header) error {
11501162 return nil
11511163}
11521164
1153- // Bounds retrieves the current head and tail tracked by the skeleton syncer.
1154- // This method is used by the backfiller, whose life cycle is controlled by the
1155- // skeleton syncer.
1165+ // Bounds retrieves the current head and tail tracked by the skeleton syncer
1166+ // and optionally the last known finalized header if any was announced and if
1167+ // it is still in the sync range. This method is used by the backfiller, whose
1168+ // life cycle is controlled by the skeleton syncer.
11561169//
11571170// Note, the method will not use the internal state of the skeleton, but will
11581171// rather blindly pull stuff from the database. This is fine, because the back-
11591172// filler will only run when the skeleton chain is fully downloaded and stable.
11601173// There might be new heads appended, but those are atomic from the perspective
11611174// of this method. Any head reorg will first tear down the backfiller and only
11621175// then make the modification.
1163- func (s * skeleton ) Bounds () (head * types.Header , tail * types.Header , err error ) {
1176+ func (s * skeleton ) Bounds () (head * types.Header , tail * types.Header , final * types. Header , err error ) {
11641177 // Read the current sync progress from disk and figure out the current head.
11651178 // Although there's a lot of error handling here, these are mostly as sanity
11661179 // checks to avoid crashing if a programming error happens. These should not
11671180 // happen in live code.
11681181 status := rawdb .ReadSkeletonSyncStatus (s .db )
11691182 if len (status ) == 0 {
1170- return nil , nil , errors .New ("beacon sync not yet started" )
1183+ return nil , nil , nil , errors .New ("beacon sync not yet started" )
11711184 }
11721185 progress := new (skeletonProgress )
11731186 if err := json .Unmarshal (status , progress ); err != nil {
1174- return nil , nil , err
1187+ return nil , nil , nil , err
11751188 }
11761189 head = rawdb .ReadSkeletonHeader (s .db , progress .Subchains [0 ].Head )
11771190 if head == nil {
1178- return nil , nil , fmt .Errorf ("head skeleton header %d is missing" , progress .Subchains [0 ].Head )
1191+ return nil , nil , nil , fmt .Errorf ("head skeleton header %d is missing" , progress .Subchains [0 ].Head )
11791192 }
11801193 tail = rawdb .ReadSkeletonHeader (s .db , progress .Subchains [0 ].Tail )
11811194 if tail == nil {
1182- return nil , nil , fmt .Errorf ("tail skeleton header %d is missing" , progress .Subchains [0 ].Tail )
1195+ return nil , nil , nil , fmt .Errorf ("tail skeleton header %d is missing" , progress .Subchains [0 ].Tail )
1196+ }
1197+ if progress .Finalized != nil && tail .Number .Uint64 () <= * progress .Finalized && * progress .Finalized <= head .Number .Uint64 () {
1198+ final = rawdb .ReadSkeletonHeader (s .db , * progress .Finalized )
1199+ if final == nil {
1200+ return nil , nil , nil , fmt .Errorf ("finalized skeleton header %d is missing" , * progress .Finalized )
1201+ }
11831202 }
1184- return head , tail , nil
1203+ return head , tail , final , nil
11851204}
11861205
11871206// Header retrieves a specific header tracked by the skeleton syncer. This method
0 commit comments