github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/eth/downloader/skeleton.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package downloader 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "math/rand" 24 "sort" 25 "time" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/core/rawdb" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/eth/protocols/eth" 31 "github.com/ethereum/go-ethereum/ethdb" 32 "github.com/ethereum/go-ethereum/log" 33 ) 34 35 // scratchHeaders is the number of headers to store in a scratch space to allow 36 // concurrent downloads. A header is about 0.5KB in size, so there is no worry 37 // about using too much memory. The only catch is that we can only validate gaps 38 // after they're linked to the head, so the bigger the scratch space, the larger 39 // potential for invalid headers. 40 // 41 // The current scratch space of 131072 headers is expected to use 64MB RAM. 42 const scratchHeaders = 131072 43 44 // requestHeaders is the number of header to request from a remote peer in a single 45 // network packet. Although the skeleton downloader takes into consideration peer 46 // capacities when picking idlers, the packet size was decided to remain constant 47 // since headers are relatively small and it's easier to work with fixed batches 48 // vs. dynamic interval fillings. 49 const requestHeaders = 512 50 51 // errSyncLinked is an internal helper error to signal that the current sync 52 // cycle linked up to the genesis block, this the skeleton syncer should ping 53 // the backfiller to resume. Since we already have that logic on sync start, 54 // piggy-back on that instead of 2 entrypoints. 55 var errSyncLinked = errors.New("sync linked") 56 57 // errSyncMerged is an internal helper error to signal that the current sync 58 // cycle merged with a previously aborted subchain, thus the skeleton syncer 59 // should abort and restart with the new state. 60 var errSyncMerged = errors.New("sync merged") 61 62 // errSyncReorged is an internal helper error to signal that the head chain of 63 // the current sync cycle was (partially) reorged, thus the skeleton syncer 64 // should abort and restart with the new state. 65 var errSyncReorged = errors.New("sync reorged") 66 67 // errTerminated is returned if the sync mechanism was terminated for this run of 68 // the process. This is usually the case when Geth is shutting down and some events 69 // might still be propagating. 70 var errTerminated = errors.New("terminated") 71 72 // errChainReorged is an internal helper error to signal that the header chain 73 // of the current sync cycle was (partially) reorged. 74 var errChainReorged = errors.New("chain reorged") 75 76 // errChainGapped is an internal helper error to signal that the header chain 77 // of the current sync cycle is gaped with the one advertised by consensus client. 78 var errChainGapped = errors.New("chain gapped") 79 80 // errChainForked is an internal helper error to signal that the header chain 81 // of the current sync cycle is forked with the one advertised by consensus client. 82 var errChainForked = errors.New("chain forked") 83 84 func init() { 85 // Tuning parameters is nice, but the scratch space must be assignable in 86 // full to peers. It's a useless cornercase to support a dangling half-group. 87 if scratchHeaders%requestHeaders != 0 { 88 panic("Please make scratchHeaders divisible by requestHeaders") 89 } 90 } 91 92 // subchain is a contiguous header chain segment that is backed by the database, 93 // but may not be linked to the live chain. The skeleton downloader may produce 94 // a new one of these every time it is restarted until the subchain grows large 95 // enough to connect with a previous subchain. 96 // 97 // The subchains use the exact same database namespace and are not disjoint from 98 // each other. As such, extending one to overlap the other entails reducing the 99 // second one first. This combined buffer model is used to avoid having to move 100 // data on disk when two subchains are joined together. 101 type subchain struct { 102 Head uint64 // Block number of the newest header in the subchain 103 Tail uint64 // Block number of the oldest header in the subchain 104 Next common.Hash // Block hash of the next oldest header in the subchain 105 } 106 107 // skeletonProgress is a database entry to allow suspending and resuming a chain 108 // sync. As the skeleton header chain is downloaded backwards, restarts can and 109 // will produce temporarily disjoint subchains. There is no way to restart a 110 // suspended skeleton sync without prior knowledge of all prior suspension points. 111 type skeletonProgress struct { 112 Subchains []*subchain // Disjoint subchains downloaded until now 113 Finalized *uint64 // Last known finalized block number 114 } 115 116 // headUpdate is a notification that the beacon sync should switch to a new target. 117 // The update might request whether to forcefully change the target, or only try to 118 // extend it and fail if it's not possible. 119 type headUpdate struct { 120 header *types.Header // Header to update the sync target to 121 final *types.Header // Finalized header to use as thresholds 122 force bool // Whether to force the update or only extend if possible 123 errc chan error // Channel to signal acceptance of the new head 124 } 125 126 // headerRequest tracks a pending header request to ensure responses are to 127 // actual requests and to validate any security constraints. 128 // 129 // Concurrency note: header requests and responses are handled concurrently from 130 // the main runloop to allow Keccak256 hash verifications on the peer's thread and 131 // to drop on invalid response. The request struct must contain all the data to 132 // construct the response without accessing runloop internals (i.e. subchains). 133 // That is only included to allow the runloop to match a response to the task being 134 // synced without having yet another set of maps. 135 type headerRequest struct { 136 peer string // Peer to which this request is assigned 137 id uint64 // Request ID of this request 138 139 deliver chan *headerResponse // Channel to deliver successful response on 140 revert chan *headerRequest // Channel to deliver request failure on 141 cancel chan struct{} // Channel to track sync cancellation 142 stale chan struct{} // Channel to signal the request was dropped 143 144 head uint64 // Head number of the requested batch of headers 145 } 146 147 // headerResponse is an already verified remote response to a header request. 148 type headerResponse struct { 149 peer *peerConnection // Peer from which this response originates 150 reqid uint64 // Request ID that this response fulfils 151 headers []*types.Header // Chain of headers 152 } 153 154 // backfiller is a callback interface through which the skeleton sync can tell 155 // the downloader that it should suspend or resume backfilling on specific head 156 // events (e.g. suspend on forks or gaps, resume on successful linkups). 157 type backfiller interface { 158 // suspend requests the backfiller to abort any running full or snap sync 159 // based on the skeleton chain as it might be invalid. The backfiller should 160 // gracefully handle multiple consecutive suspends without a resume, even 161 // on initial startup. 162 // 163 // The method should return the last block header that has been successfully 164 // backfilled (in the current or a previous run), falling back to the genesis. 165 suspend() *types.Header 166 167 // resume requests the backfiller to start running fill or snap sync based on 168 // the skeleton chain as it has successfully been linked. Appending new heads 169 // to the end of the chain will not result in suspend/resume cycles. 170 // leaking too much sync logic out to the filler. 171 resume() 172 } 173 174 // skeleton represents a header chain synchronized after the merge where blocks 175 // aren't validated any more via PoW in a forward fashion, rather are dictated 176 // and extended at the head via the beacon chain and backfilled on the original 177 // Ethereum block sync protocol. 178 // 179 // Since the skeleton is grown backwards from head to genesis, it is handled as 180 // a separate entity, not mixed in with the logical sequential transition of the 181 // blocks. Once the skeleton is connected to an existing, validated chain, the 182 // headers will be moved into the main downloader for filling and execution. 183 // 184 // Opposed to the original Ethereum block synchronization which is trustless (and 185 // uses a master peer to minimize the attack surface), post-merge block sync starts 186 // from a trusted head. As such, there is no need for a master peer any more and 187 // headers can be requested fully concurrently (though some batches might be 188 // discarded if they don't link up correctly). 189 // 190 // Although a skeleton is part of a sync cycle, it is not recreated, rather stays 191 // alive throughout the lifetime of the downloader. This allows it to be extended 192 // concurrently with the sync cycle, since extensions arrive from an API surface, 193 // not from within (vs. legacy Ethereum sync). 194 // 195 // Since the skeleton tracks the entire header chain until it is consumed by the 196 // forward block filling, it needs 0.5KB/block storage. At current mainnet sizes 197 // this is only possible with a disk backend. Since the skeleton is separate from 198 // the node's header chain, storing the headers ephemerally until sync finishes 199 // is wasted disk IO, but it's a price we're going to pay to keep things simple 200 // for now. 201 type skeleton struct { 202 db ethdb.Database // Database backing the skeleton 203 filler backfiller // Chain syncer suspended/resumed by head events 204 205 peers *peerSet // Set of peers we can sync from 206 idles map[string]*peerConnection // Set of idle peers in the current sync cycle 207 drop peerDropFn // Drops a peer for misbehaving 208 209 progress *skeletonProgress // Sync progress tracker for resumption and metrics 210 started time.Time // Timestamp when the skeleton syncer was created 211 logged time.Time // Timestamp when progress was last logged to the user 212 pulled uint64 // Number of headers downloaded in this run 213 214 scratchSpace []*types.Header // Scratch space to accumulate headers in (first = recent) 215 scratchOwners []string // Peer IDs owning chunks of the scratch space (pend or delivered) 216 scratchHead uint64 // Block number of the first item in the scratch space 217 218 requests map[uint64]*headerRequest // Header requests currently running 219 220 headEvents chan *headUpdate // Notification channel for new heads 221 terminate chan chan error // Termination channel to abort sync 222 terminated chan struct{} // Channel to signal that the syncer is dead 223 224 // Callback hooks used during testing 225 syncStarting func() // callback triggered after a sync cycle is inited but before started 226 } 227 228 // newSkeleton creates a new sync skeleton that tracks a potentially dangling 229 // header chain until it's linked into an existing set of blocks. 230 func newSkeleton(db ethdb.Database, peers *peerSet, drop peerDropFn, filler backfiller) *skeleton { 231 sk := &skeleton{ 232 db: db, 233 filler: filler, 234 peers: peers, 235 drop: drop, 236 requests: make(map[uint64]*headerRequest), 237 headEvents: make(chan *headUpdate), 238 terminate: make(chan chan error), 239 terminated: make(chan struct{}), 240 } 241 go sk.startup() 242 return sk 243 } 244 245 // startup is an initial background loop which waits for an event to start or 246 // tear the syncer down. This is required to make the skeleton sync loop once 247 // per process but at the same time not start before the beacon chain announces 248 // a new (existing) head. 249 func (s *skeleton) startup() { 250 // Close a notification channel so anyone sending us events will know if the 251 // sync loop was torn down for good. 252 defer close(s.terminated) 253 254 // Wait for startup or teardown. This wait might loop a few times if a beacon 255 // client requests sync head extensions, but not forced reorgs (i.e. they are 256 // giving us new payloads without setting a starting head initially). 257 for { 258 select { 259 case errc := <-s.terminate: 260 // No head was announced but Geth is shutting down 261 errc <- nil 262 return 263 264 case event := <-s.headEvents: 265 // New head announced, start syncing to it, looping every time a current 266 // cycle is terminated due to a chain event (head reorg, old chain merge). 267 if !event.force { 268 event.errc <- errors.New("forced head needed for startup") 269 continue 270 } 271 event.errc <- nil // forced head accepted for startup 272 head := event.header 273 s.started = time.Now() 274 275 for { 276 // If the sync cycle terminated or was terminated, propagate up when 277 // higher layers request termination. There's no fancy explicit error 278 // signalling as the sync loop should never terminate (TM). 279 newhead, err := s.sync(head) 280 switch { 281 case err == errSyncLinked: 282 // Sync cycle linked up to the genesis block, or the existent chain 283 // segment. Tear down the loop and restart it so, it can properly 284 // notify the backfiller. Don't account a new head. 285 head = nil 286 287 case err == errSyncMerged: 288 // Subchains were merged, we just need to reinit the internal 289 // start to continue on the tail of the merged chain. Don't 290 // announce a new head, 291 head = nil 292 293 case err == errSyncReorged: 294 // The subchain being synced got modified at the head in a 295 // way that requires resyncing it. Restart sync with the new 296 // head to force a cleanup. 297 head = newhead 298 299 case err == errTerminated: 300 // Sync was requested to be terminated from within, stop and 301 // return (no need to pass a message, was already done internally) 302 return 303 304 default: 305 // Sync either successfully terminated or failed with an unhandled 306 // error. Abort and wait until Geth requests a termination. 307 errc := <-s.terminate 308 errc <- err 309 return 310 } 311 } 312 } 313 } 314 } 315 316 // Terminate tears down the syncer indefinitely. 317 func (s *skeleton) Terminate() error { 318 // Request termination and fetch any errors 319 errc := make(chan error) 320 s.terminate <- errc 321 err := <-errc 322 323 // Wait for full shutdown (not necessary, but cleaner) 324 <-s.terminated 325 return err 326 } 327 328 // Sync starts or resumes a previous sync cycle to download and maintain a reverse 329 // header chain starting at the head and leading towards genesis to an available 330 // ancestor. 331 // 332 // This method does not block, rather it just waits until the syncer receives the 333 // fed header. What the syncer does with it is the syncer's problem. 334 func (s *skeleton) Sync(head *types.Header, final *types.Header, force bool) error { 335 log.Trace("New skeleton head announced", "number", head.Number, "hash", head.Hash(), "force", force) 336 errc := make(chan error) 337 338 select { 339 case s.headEvents <- &headUpdate{header: head, final: final, force: force, errc: errc}: 340 return <-errc 341 case <-s.terminated: 342 return errTerminated 343 } 344 } 345 346 // sync is the internal version of Sync that executes a single sync cycle, either 347 // until some termination condition is reached, or until the current cycle merges 348 // with a previously aborted run. 349 func (s *skeleton) sync(head *types.Header) (*types.Header, error) { 350 // If we're continuing a previous merge interrupt, just access the existing 351 // old state without initing from disk. 352 if head == nil { 353 head = rawdb.ReadSkeletonHeader(s.db, s.progress.Subchains[0].Head) 354 } else { 355 // Otherwise, initialize the sync, trimming and previous leftovers until 356 // we're consistent with the newly requested chain head 357 s.initSync(head) 358 } 359 // Create the scratch space to fill with concurrently downloaded headers 360 s.scratchSpace = make([]*types.Header, scratchHeaders) 361 defer func() { s.scratchSpace = nil }() // don't hold on to references after sync 362 363 s.scratchOwners = make([]string, scratchHeaders/requestHeaders) 364 defer func() { s.scratchOwners = nil }() // don't hold on to references after sync 365 366 s.scratchHead = s.progress.Subchains[0].Tail - 1 // tail must not be 0! 367 368 // If the sync is already done, resume the backfiller. When the loop stops, 369 // terminate the backfiller too. 370 linked := len(s.progress.Subchains) == 1 && 371 rawdb.HasHeader(s.db, s.progress.Subchains[0].Next, s.scratchHead) && 372 rawdb.HasBody(s.db, s.progress.Subchains[0].Next, s.scratchHead) && 373 rawdb.HasReceipts(s.db, s.progress.Subchains[0].Next, s.scratchHead) 374 if linked { 375 s.filler.resume() 376 } 377 defer func() { 378 // The filler needs to be suspended, but since it can block for a while 379 // when there are many blocks queued up for full-sync importing, run it 380 // on a separate goroutine and consume head messages that need instant 381 // replies. 382 done := make(chan struct{}) 383 go func() { 384 defer close(done) 385 filled := s.filler.suspend() 386 if filled == nil { 387 log.Error("Latest filled block is not available") 388 return 389 } 390 // If something was filled, try to delete stale sync helpers. If 391 // unsuccessful, warn the user, but not much else we can do (it's 392 // a programming error, just let users report an issue and don't 393 // choke in the meantime). 394 if err := s.cleanStales(filled); err != nil { 395 log.Error("Failed to clean stale beacon headers", "err", err) 396 } 397 }() 398 // Wait for the suspend to finish, consuming head events in the meantime 399 // and dropping them on the floor. 400 for { 401 select { 402 case <-done: 403 return 404 case event := <-s.headEvents: 405 event.errc <- errors.New("beacon syncer reorging") 406 } 407 } 408 }() 409 // Create a set of unique channels for this sync cycle. We need these to be 410 // ephemeral so a data race doesn't accidentally deliver something stale on 411 // a persistent channel across syncs (yup, this happened) 412 var ( 413 requestFails = make(chan *headerRequest) 414 responses = make(chan *headerResponse) 415 ) 416 cancel := make(chan struct{}) 417 defer close(cancel) 418 419 log.Debug("Starting reverse header sync cycle", "head", head.Number, "hash", head.Hash(), "cont", s.scratchHead) 420 421 // Whether sync completed or not, disregard any future packets 422 defer func() { 423 log.Debug("Terminating reverse header sync cycle", "head", head.Number, "hash", head.Hash(), "cont", s.scratchHead) 424 s.requests = make(map[uint64]*headerRequest) 425 }() 426 427 // Start tracking idle peers for task assignments 428 peering := make(chan *peeringEvent, 64) // arbitrary buffer, just some burst protection 429 430 peeringSub := s.peers.SubscribeEvents(peering) 431 defer peeringSub.Unsubscribe() 432 433 s.idles = make(map[string]*peerConnection) 434 for _, peer := range s.peers.AllPeers() { 435 s.idles[peer.id] = peer 436 } 437 // Notify any tester listening for startup events 438 if s.syncStarting != nil { 439 s.syncStarting() 440 } 441 for { 442 // Something happened, try to assign new tasks to any idle peers 443 if !linked { 444 s.assignTasks(responses, requestFails, cancel) 445 } 446 // Wait for something to happen 447 select { 448 case event := <-peering: 449 // A peer joined or left, the tasks queue and allocations need to be 450 // checked for potential assignment or reassignment 451 peerid := event.peer.id 452 if event.join { 453 log.Debug("Joining skeleton peer", "id", peerid) 454 s.idles[peerid] = event.peer 455 } else { 456 log.Debug("Leaving skeleton peer", "id", peerid) 457 s.revertRequests(peerid) 458 delete(s.idles, peerid) 459 } 460 461 case errc := <-s.terminate: 462 errc <- nil 463 return nil, errTerminated 464 465 case event := <-s.headEvents: 466 // New head was announced, try to integrate it. If successful, nothing 467 // needs to be done as the head simply extended the last range. For now 468 // we don't seamlessly integrate reorgs to keep things simple. If the 469 // network starts doing many mini reorgs, it might be worthwhile handling 470 // a limited depth without an error. 471 if err := s.processNewHead(event.header, event.final); err != nil { 472 // If a reorg is needed, and we're forcing the new head, signal 473 // the syncer to tear down and start over. Otherwise, drop the 474 // non-force reorg. 475 if event.force { 476 event.errc <- nil // forced head reorg accepted 477 log.Info("Restarting sync cycle", "reason", err) 478 return event.header, errSyncReorged 479 } 480 event.errc <- err 481 continue 482 } 483 event.errc <- nil // head extension accepted 484 485 // New head was integrated into the skeleton chain. If the backfiller 486 // is still running, it will pick it up. If it already terminated, 487 // a new cycle needs to be spun up. 488 if linked { 489 s.filler.resume() 490 } 491 492 case req := <-requestFails: 493 s.revertRequest(req) 494 495 case res := <-responses: 496 // Process the batch of headers. If though processing we managed to 497 // link the current subchain to a previously downloaded one, abort the 498 // sync and restart with the merged subchains. 499 // 500 // If we managed to link to the existing local chain or genesis block, 501 // abort sync altogether. 502 linked, merged := s.processResponse(res) 503 if linked { 504 log.Debug("Beacon sync linked to local chain") 505 return nil, errSyncLinked 506 } 507 if merged { 508 log.Debug("Beacon sync merged subchains") 509 return nil, errSyncMerged 510 } 511 // We still have work to do, loop and repeat 512 } 513 } 514 } 515 516 // initSync attempts to get the skeleton sync into a consistent state wrt any 517 // past state on disk and the newly requested head to sync to. If the new head 518 // is nil, the method will return and continue from the previous head. 519 func (s *skeleton) initSync(head *types.Header) { 520 // Extract the head number, we'll need it all over 521 number := head.Number.Uint64() 522 523 // Retrieve the previously saved sync progress 524 if status := rawdb.ReadSkeletonSyncStatus(s.db); len(status) > 0 { 525 s.progress = new(skeletonProgress) 526 if err := json.Unmarshal(status, s.progress); err != nil { 527 log.Error("Failed to decode skeleton sync status", "err", err) 528 } else { 529 // Previous sync was available, print some continuation logs 530 for _, subchain := range s.progress.Subchains { 531 log.Debug("Restarting skeleton subchain", "head", subchain.Head, "tail", subchain.Tail) 532 } 533 // Create a new subchain for the head (unless the last can be extended), 534 // trimming anything it would overwrite 535 headchain := &subchain{ 536 Head: number, 537 Tail: number, 538 Next: head.ParentHash, 539 } 540 for len(s.progress.Subchains) > 0 { 541 // If the last chain is above the new head, delete altogether 542 lastchain := s.progress.Subchains[0] 543 if lastchain.Tail >= headchain.Tail { 544 log.Debug("Dropping skeleton subchain", "head", lastchain.Head, "tail", lastchain.Tail) 545 s.progress.Subchains = s.progress.Subchains[1:] 546 continue 547 } 548 // Otherwise truncate the last chain if needed and abort trimming 549 if lastchain.Head >= headchain.Tail { 550 log.Debug("Trimming skeleton subchain", "oldhead", lastchain.Head, "newhead", headchain.Tail-1, "tail", lastchain.Tail) 551 lastchain.Head = headchain.Tail - 1 552 } 553 break 554 } 555 // If the last subchain can be extended, we're lucky. Otherwise, create 556 // a new subchain sync task. 557 var extended bool 558 if n := len(s.progress.Subchains); n > 0 { 559 lastchain := s.progress.Subchains[0] 560 if lastchain.Head == headchain.Tail-1 { 561 lasthead := rawdb.ReadSkeletonHeader(s.db, lastchain.Head) 562 if lasthead.Hash() == head.ParentHash { 563 log.Debug("Extended skeleton subchain with new head", "head", headchain.Tail, "tail", lastchain.Tail) 564 lastchain.Head = headchain.Tail 565 extended = true 566 } 567 } 568 } 569 if !extended { 570 log.Debug("Created new skeleton subchain", "head", number, "tail", number) 571 s.progress.Subchains = append([]*subchain{headchain}, s.progress.Subchains...) 572 } 573 // Update the database with the new sync stats and insert the new 574 // head header. We won't delete any trimmed skeleton headers since 575 // those will be outside the index space of the many subchains and 576 // the database space will be reclaimed eventually when processing 577 // blocks above the current head (TODO(karalabe): don't forget). 578 batch := s.db.NewBatch() 579 580 rawdb.WriteSkeletonHeader(batch, head) 581 s.saveSyncStatus(batch) 582 583 if err := batch.Write(); err != nil { 584 log.Crit("Failed to write skeleton sync status", "err", err) 585 } 586 return 587 } 588 } 589 // Either we've failed to decode the previous state, or there was none. Start 590 // a fresh sync with a single subchain represented by the currently sent 591 // chain head. 592 s.progress = &skeletonProgress{ 593 Subchains: []*subchain{ 594 { 595 Head: number, 596 Tail: number, 597 Next: head.ParentHash, 598 }, 599 }, 600 } 601 batch := s.db.NewBatch() 602 603 rawdb.WriteSkeletonHeader(batch, head) 604 s.saveSyncStatus(batch) 605 606 if err := batch.Write(); err != nil { 607 log.Crit("Failed to write initial skeleton sync status", "err", err) 608 } 609 log.Debug("Created initial skeleton subchain", "head", number, "tail", number) 610 } 611 612 // saveSyncStatus marshals the remaining sync tasks into leveldb. 613 func (s *skeleton) saveSyncStatus(db ethdb.KeyValueWriter) { 614 status, err := json.Marshal(s.progress) 615 if err != nil { 616 panic(err) // This can only fail during implementation 617 } 618 rawdb.WriteSkeletonSyncStatus(db, status) 619 } 620 621 // processNewHead does the internal shuffling for a new head marker and either 622 // accepts and integrates it into the skeleton or requests a reorg. Upon reorg, 623 // the syncer will tear itself down and restart with a fresh head. It is simpler 624 // to reconstruct the sync state than to mutate it and hope for the best. 625 func (s *skeleton) processNewHead(head *types.Header, final *types.Header) error { 626 // If a new finalized block was announced, update the sync process independent 627 // of what happens with the sync head below 628 if final != nil { 629 if number := final.Number.Uint64(); s.progress.Finalized == nil || *s.progress.Finalized != number { 630 s.progress.Finalized = new(uint64) 631 *s.progress.Finalized = final.Number.Uint64() 632 633 s.saveSyncStatus(s.db) 634 } 635 } 636 // If the header cannot be inserted without interruption, return an error for 637 // the outer loop to tear down the skeleton sync and restart it 638 number := head.Number.Uint64() 639 640 lastchain := s.progress.Subchains[0] 641 if lastchain.Tail >= number { 642 // If the chain is down to a single beacon header, and it is re-announced 643 // once more, ignore it instead of tearing down sync for a noop. 644 if lastchain.Head == lastchain.Tail { 645 if current := rawdb.ReadSkeletonHeader(s.db, number); current.Hash() == head.Hash() { 646 return nil 647 } 648 } 649 // Not a noop / double head announce, abort with a reorg 650 return fmt.Errorf("%w, tail: %d, head: %d, newHead: %d", errChainReorged, lastchain.Tail, lastchain.Head, number) 651 } 652 if lastchain.Head+1 < number { 653 return fmt.Errorf("%w, head: %d, newHead: %d", errChainGapped, lastchain.Head, number) 654 } 655 if parent := rawdb.ReadSkeletonHeader(s.db, number-1); parent.Hash() != head.ParentHash { 656 return fmt.Errorf("%w, ancestor: %d, hash: %s, want: %s", errChainForked, number-1, parent.Hash(), head.ParentHash) 657 } 658 // New header seems to be in the last subchain range. Unwind any extra headers 659 // from the chain tip and insert the new head. We won't delete any trimmed 660 // skeleton headers since those will be outside the index space of the many 661 // subchains and the database space will be reclaimed eventually when processing 662 // blocks above the current head (TODO(karalabe): don't forget). 663 batch := s.db.NewBatch() 664 665 rawdb.WriteSkeletonHeader(batch, head) 666 lastchain.Head = number 667 s.saveSyncStatus(batch) 668 669 if err := batch.Write(); err != nil { 670 log.Crit("Failed to write skeleton sync status", "err", err) 671 } 672 return nil 673 } 674 675 // assignTasks attempts to match idle peers to pending header retrievals. 676 func (s *skeleton) assignTasks(success chan *headerResponse, fail chan *headerRequest, cancel chan struct{}) { 677 // Sort the peers by download capacity to use faster ones if many available 678 idlers := &peerCapacitySort{ 679 peers: make([]*peerConnection, 0, len(s.idles)), 680 caps: make([]int, 0, len(s.idles)), 681 } 682 targetTTL := s.peers.rates.TargetTimeout() 683 for _, peer := range s.idles { 684 idlers.peers = append(idlers.peers, peer) 685 idlers.caps = append(idlers.caps, s.peers.rates.Capacity(peer.id, eth.BlockHeadersMsg, targetTTL)) 686 } 687 if len(idlers.peers) == 0 { 688 return 689 } 690 sort.Sort(idlers) 691 692 // Find header regions not yet downloading and fill them 693 for task, owner := range s.scratchOwners { 694 // If we're out of idle peers, stop assigning tasks 695 if len(idlers.peers) == 0 { 696 return 697 } 698 // Skip any tasks already filling 699 if owner != "" { 700 continue 701 } 702 // If we've reached the genesis, stop assigning tasks 703 if uint64(task*requestHeaders) >= s.scratchHead { 704 return 705 } 706 // Found a task and have peers available, assign it 707 idle := idlers.peers[0] 708 709 idlers.peers = idlers.peers[1:] 710 idlers.caps = idlers.caps[1:] 711 712 // Matched a pending task to an idle peer, allocate a unique request id 713 var reqid uint64 714 for { 715 reqid = uint64(rand.Int63()) 716 if reqid == 0 { 717 continue 718 } 719 if _, ok := s.requests[reqid]; ok { 720 continue 721 } 722 break 723 } 724 // Generate the network query and send it to the peer 725 req := &headerRequest{ 726 peer: idle.id, 727 id: reqid, 728 deliver: success, 729 revert: fail, 730 cancel: cancel, 731 stale: make(chan struct{}), 732 head: s.scratchHead - uint64(task*requestHeaders), 733 } 734 s.requests[reqid] = req 735 delete(s.idles, idle.id) 736 737 // Generate the network query and send it to the peer 738 go s.executeTask(idle, req) 739 740 // Inject the request into the task to block further assignments 741 s.scratchOwners[task] = idle.id 742 } 743 } 744 745 // executeTask executes a single fetch request, blocking until either a result 746 // arrives or a timeouts / cancellation is triggered. The method should be run 747 // on its own goroutine and will deliver on the requested channels. 748 func (s *skeleton) executeTask(peer *peerConnection, req *headerRequest) { 749 start := time.Now() 750 resCh := make(chan *eth.Response) 751 752 // Figure out how many headers to fetch. Usually this will be a full batch, 753 // but for the very tail of the chain, trim the request to the number left. 754 // Since nodes may or may not return the genesis header for a batch request, 755 // don't even request it. The parent hash of block #1 is enough to link. 756 requestCount := requestHeaders 757 if req.head < requestHeaders { 758 requestCount = int(req.head) 759 } 760 peer.log.Trace("Fetching skeleton headers", "from", req.head, "count", requestCount) 761 netreq, err := peer.peer.RequestHeadersByNumber(req.head, requestCount, 0, true, resCh) 762 if err != nil { 763 peer.log.Trace("Failed to request headers", "err", err) 764 s.scheduleRevertRequest(req) 765 return 766 } 767 defer netreq.Close() 768 769 // Wait until the response arrives, the request is cancelled or times out 770 ttl := s.peers.rates.TargetTimeout() 771 772 timeoutTimer := time.NewTimer(ttl) 773 defer timeoutTimer.Stop() 774 775 select { 776 case <-req.cancel: 777 peer.log.Debug("Header request cancelled") 778 s.scheduleRevertRequest(req) 779 780 case <-timeoutTimer.C: 781 // Header retrieval timed out, update the metrics 782 peer.log.Warn("Header request timed out, dropping peer", "elapsed", ttl) 783 headerTimeoutMeter.Mark(1) 784 s.peers.rates.Update(peer.id, eth.BlockHeadersMsg, 0, 0) 785 s.scheduleRevertRequest(req) 786 787 // At this point we either need to drop the offending peer, or we need a 788 // mechanism to allow waiting for the response and not cancel it. For now 789 // lets go with dropping since the header sizes are deterministic and the 790 // beacon sync runs exclusive (downloader is idle) so there should be no 791 // other load to make timeouts probable. If we notice that timeouts happen 792 // more often than we'd like, we can introduce a tracker for the requests 793 // gone stale and monitor them. However, in that case too, we need a way 794 // to protect against malicious peers never responding, so it would need 795 // a second, hard-timeout mechanism. 796 s.drop(peer.id) 797 798 case res := <-resCh: 799 // Headers successfully retrieved, update the metrics 800 headers := *res.Res.(*eth.BlockHeadersRequest) 801 802 headerReqTimer.Update(time.Since(start)) 803 s.peers.rates.Update(peer.id, eth.BlockHeadersMsg, res.Time, len(headers)) 804 805 // Cross validate the headers with the requests 806 switch { 807 case len(headers) == 0: 808 // No headers were delivered, reject the response and reschedule 809 peer.log.Debug("No headers delivered") 810 res.Done <- errors.New("no headers delivered") 811 s.scheduleRevertRequest(req) 812 813 case headers[0].Number.Uint64() != req.head: 814 // Header batch anchored at non-requested number 815 peer.log.Debug("Invalid header response head", "have", headers[0].Number, "want", req.head) 816 res.Done <- errors.New("invalid header batch anchor") 817 s.scheduleRevertRequest(req) 818 819 case req.head >= requestHeaders && len(headers) != requestHeaders: 820 // Invalid number of non-genesis headers delivered, reject the response and reschedule 821 peer.log.Debug("Invalid non-genesis header count", "have", len(headers), "want", requestHeaders) 822 res.Done <- errors.New("not enough non-genesis headers delivered") 823 s.scheduleRevertRequest(req) 824 825 case req.head < requestHeaders && uint64(len(headers)) != req.head: 826 // Invalid number of genesis headers delivered, reject the response and reschedule 827 peer.log.Debug("Invalid genesis header count", "have", len(headers), "want", headers[0].Number.Uint64()) 828 res.Done <- errors.New("not enough genesis headers delivered") 829 s.scheduleRevertRequest(req) 830 831 default: 832 // Packet seems structurally valid, check hash progression and if it 833 // is correct too, deliver for storage 834 for i := 0; i < len(headers)-1; i++ { 835 if headers[i].ParentHash != headers[i+1].Hash() { 836 peer.log.Debug("Invalid hash progression", "index", i, "wantparenthash", headers[i].ParentHash, "haveparenthash", headers[i+1].Hash()) 837 res.Done <- errors.New("invalid hash progression") 838 s.scheduleRevertRequest(req) 839 return 840 } 841 } 842 // Hash chain is valid. The delivery might still be junk as we're 843 // downloading batches concurrently (so no way to link the headers 844 // until gaps are filled); in that case, we'll nuke the peer when 845 // we detect the fault. 846 res.Done <- nil 847 848 select { 849 case req.deliver <- &headerResponse{ 850 peer: peer, 851 reqid: req.id, 852 headers: headers, 853 }: 854 case <-req.cancel: 855 } 856 } 857 } 858 } 859 860 // revertRequests locates all the currently pending requests from a particular 861 // peer and reverts them, rescheduling for others to fulfill. 862 func (s *skeleton) revertRequests(peer string) { 863 // Gather the requests first, revertals need the lock too 864 var requests []*headerRequest 865 for _, req := range s.requests { 866 if req.peer == peer { 867 requests = append(requests, req) 868 } 869 } 870 // Revert all the requests matching the peer 871 for _, req := range requests { 872 s.revertRequest(req) 873 } 874 } 875 876 // scheduleRevertRequest asks the event loop to clean up a request and return 877 // all failed retrieval tasks to the scheduler for reassignment. 878 func (s *skeleton) scheduleRevertRequest(req *headerRequest) { 879 select { 880 case req.revert <- req: 881 // Sync event loop notified 882 case <-req.cancel: 883 // Sync cycle got cancelled 884 case <-req.stale: 885 // Request already reverted 886 } 887 } 888 889 // revertRequest cleans up a request and returns all failed retrieval tasks to 890 // the scheduler for reassignment. 891 // 892 // Note, this needs to run on the event runloop thread to reschedule to idle peers. 893 // On peer threads, use scheduleRevertRequest. 894 func (s *skeleton) revertRequest(req *headerRequest) { 895 log.Trace("Reverting header request", "peer", req.peer, "reqid", req.id) 896 select { 897 case <-req.stale: 898 log.Trace("Header request already reverted", "peer", req.peer, "reqid", req.id) 899 return 900 default: 901 } 902 close(req.stale) 903 904 // Remove the request from the tracked set 905 delete(s.requests, req.id) 906 907 // Remove the request from the tracked set and mark the task as not-pending, 908 // ready for rescheduling 909 s.scratchOwners[(s.scratchHead-req.head)/requestHeaders] = "" 910 } 911 912 func (s *skeleton) processResponse(res *headerResponse) (linked bool, merged bool) { 913 res.peer.log.Trace("Processing header response", "head", res.headers[0].Number, "hash", res.headers[0].Hash(), "count", len(res.headers)) 914 915 // Whether the response is valid, we can mark the peer as idle and notify 916 // the scheduler to assign a new task. If the response is invalid, we'll 917 // drop the peer in a bit. 918 s.idles[res.peer.id] = res.peer 919 920 // Ensure the response is for a valid request 921 if _, ok := s.requests[res.reqid]; !ok { 922 // Some internal accounting is broken. A request either times out or it 923 // gets fulfilled successfully. It should not be possible to deliver a 924 // response to a non-existing request. 925 res.peer.log.Error("Unexpected header packet") 926 return false, false 927 } 928 delete(s.requests, res.reqid) 929 930 // Insert the delivered headers into the scratch space independent of the 931 // content or continuation; those will be validated in a moment 932 head := res.headers[0].Number.Uint64() 933 copy(s.scratchSpace[s.scratchHead-head:], res.headers) 934 935 // If there's still a gap in the head of the scratch space, abort 936 if s.scratchSpace[0] == nil { 937 return false, false 938 } 939 // Try to consume any head headers, validating the boundary conditions 940 batch := s.db.NewBatch() 941 for s.scratchSpace[0] != nil { 942 // Next batch of headers available, cross-reference with the subchain 943 // we are extending and either accept or discard 944 if s.progress.Subchains[0].Next != s.scratchSpace[0].Hash() { 945 // Print a log messages to track what's going on 946 tail := s.progress.Subchains[0].Tail 947 want := s.progress.Subchains[0].Next 948 have := s.scratchSpace[0].Hash() 949 950 log.Warn("Invalid skeleton headers", "peer", s.scratchOwners[0], "number", tail-1, "want", want, "have", have) 951 952 // The peer delivered junk, or at least not the subchain we are 953 // syncing to. Free up the scratch space and assignment, reassign 954 // and drop the original peer. 955 for i := 0; i < requestHeaders; i++ { 956 s.scratchSpace[i] = nil 957 } 958 s.drop(s.scratchOwners[0]) 959 s.scratchOwners[0] = "" 960 break 961 } 962 // Scratch delivery matches required subchain, deliver the batch of 963 // headers and push the subchain forward 964 var consumed int 965 for _, header := range s.scratchSpace[:requestHeaders] { 966 if header != nil { // nil when the genesis is reached 967 consumed++ 968 969 rawdb.WriteSkeletonHeader(batch, header) 970 s.pulled++ 971 972 s.progress.Subchains[0].Tail-- 973 s.progress.Subchains[0].Next = header.ParentHash 974 975 // If we've reached an existing block in the chain, stop retrieving 976 // headers. Note, if we want to support light clients with the same 977 // code we'd need to switch here based on the downloader mode. That 978 // said, there's no such functionality for now, so don't complicate. 979 // 980 // In the case of full sync it would be enough to check for the body, 981 // but even a full syncing node will generate a receipt once block 982 // processing is done, so it's just one more "needless" check. 983 // 984 // The weird cascading checks are done to minimize the database reads. 985 linked = rawdb.HasHeader(s.db, header.ParentHash, header.Number.Uint64()-1) && 986 rawdb.HasBody(s.db, header.ParentHash, header.Number.Uint64()-1) && 987 rawdb.HasReceipts(s.db, header.ParentHash, header.Number.Uint64()-1) 988 if linked { 989 break 990 } 991 } 992 } 993 head := s.progress.Subchains[0].Head 994 tail := s.progress.Subchains[0].Tail 995 next := s.progress.Subchains[0].Next 996 997 log.Trace("Primary subchain extended", "head", head, "tail", tail, "next", next) 998 999 // If the beacon chain was linked to the local chain, completely swap out 1000 // all internal progress and abort header synchronization. 1001 if linked { 1002 // Linking into the local chain should also mean that there are no 1003 // leftover subchains, but in the case of importing the blocks via 1004 // the engine API, we will not push the subchains forward. This will 1005 // lead to a gap between an old sync cycle and a future one. 1006 if subchains := len(s.progress.Subchains); subchains > 1 { 1007 switch { 1008 // If there are only 2 subchains - the current one and an older 1009 // one - and the old one consists of a single block, then it's 1010 // the expected new sync cycle after some propagated blocks. Log 1011 // it for debugging purposes, explicitly clean and don't escalate. 1012 case subchains == 2 && s.progress.Subchains[1].Head == s.progress.Subchains[1].Tail: 1013 // Remove the leftover skeleton header associated with old 1014 // skeleton chain only if it's not covered by the current 1015 // skeleton range. 1016 if s.progress.Subchains[1].Head < s.progress.Subchains[0].Tail { 1017 log.Debug("Cleaning previous beacon sync state", "head", s.progress.Subchains[1].Head) 1018 rawdb.DeleteSkeletonHeader(batch, s.progress.Subchains[1].Head) 1019 } 1020 // Drop the leftover skeleton chain since it's stale. 1021 s.progress.Subchains = s.progress.Subchains[:1] 1022 1023 // If we have more than one header or more than one leftover chain, 1024 // the syncer's internal state is corrupted. Do try to fix it, but 1025 // be very vocal about the fault. 1026 default: 1027 var context []interface{} 1028 1029 for i := range s.progress.Subchains[1:] { 1030 context = append(context, fmt.Sprintf("stale_head_%d", i+1)) 1031 context = append(context, s.progress.Subchains[i+1].Head) 1032 context = append(context, fmt.Sprintf("stale_tail_%d", i+1)) 1033 context = append(context, s.progress.Subchains[i+1].Tail) 1034 context = append(context, fmt.Sprintf("stale_next_%d", i+1)) 1035 context = append(context, s.progress.Subchains[i+1].Next) 1036 } 1037 log.Error("Cleaning spurious beacon sync leftovers", context...) 1038 s.progress.Subchains = s.progress.Subchains[:1] 1039 1040 // Note, here we didn't actually delete the headers at all, 1041 // just the metadata. We could implement a cleanup mechanism, 1042 // but further modifying corrupted state is kind of asking 1043 // for it. Unless there's a good enough reason to risk it, 1044 // better to live with the small database junk. 1045 } 1046 } 1047 break 1048 } 1049 // Batch of headers consumed, shift the download window forward 1050 copy(s.scratchSpace, s.scratchSpace[requestHeaders:]) 1051 for i := 0; i < requestHeaders; i++ { 1052 s.scratchSpace[scratchHeaders-i-1] = nil 1053 } 1054 copy(s.scratchOwners, s.scratchOwners[1:]) 1055 s.scratchOwners[scratchHeaders/requestHeaders-1] = "" 1056 1057 s.scratchHead -= uint64(consumed) 1058 1059 // If the subchain extended into the next subchain, we need to handle 1060 // the overlap. Since there could be many overlaps (come on), do this 1061 // in a loop. 1062 for len(s.progress.Subchains) > 1 && s.progress.Subchains[1].Head >= s.progress.Subchains[0].Tail { 1063 // Extract some stats from the second subchain 1064 head := s.progress.Subchains[1].Head 1065 tail := s.progress.Subchains[1].Tail 1066 next := s.progress.Subchains[1].Next 1067 1068 // Since we just overwrote part of the next subchain, we need to trim 1069 // its head independent of matching or mismatching content 1070 if s.progress.Subchains[1].Tail >= s.progress.Subchains[0].Tail { 1071 // Fully overwritten, get rid of the subchain as a whole 1072 log.Debug("Previous subchain fully overwritten", "head", head, "tail", tail, "next", next) 1073 s.progress.Subchains = append(s.progress.Subchains[:1], s.progress.Subchains[2:]...) 1074 continue 1075 } else { 1076 // Partially overwritten, trim the head to the overwritten size 1077 log.Debug("Previous subchain partially overwritten", "head", head, "tail", tail, "next", next) 1078 s.progress.Subchains[1].Head = s.progress.Subchains[0].Tail - 1 1079 } 1080 // If the old subchain is an extension of the new one, merge the two 1081 // and let the skeleton syncer restart (to clean internal state) 1082 if rawdb.ReadSkeletonHeader(s.db, s.progress.Subchains[1].Head).Hash() == s.progress.Subchains[0].Next { 1083 log.Debug("Previous subchain merged", "head", head, "tail", tail, "next", next) 1084 s.progress.Subchains[0].Tail = s.progress.Subchains[1].Tail 1085 s.progress.Subchains[0].Next = s.progress.Subchains[1].Next 1086 1087 s.progress.Subchains = append(s.progress.Subchains[:1], s.progress.Subchains[2:]...) 1088 merged = true 1089 } 1090 } 1091 // If subchains were merged, all further available headers in the scratch 1092 // space are invalid since we skipped ahead. Stop processing the scratch 1093 // space to avoid dropping peers thinking they delivered invalid data. 1094 if merged { 1095 break 1096 } 1097 } 1098 s.saveSyncStatus(batch) 1099 if err := batch.Write(); err != nil { 1100 log.Crit("Failed to write skeleton headers and progress", "err", err) 1101 } 1102 // Print a progress report making the UX a bit nicer 1103 left := s.progress.Subchains[0].Tail - 1 1104 if linked { 1105 left = 0 1106 } 1107 if time.Since(s.logged) > 8*time.Second || left == 0 { 1108 s.logged = time.Now() 1109 1110 if s.pulled == 0 { 1111 log.Info("Beacon sync starting", "left", left) 1112 } else { 1113 eta := float64(time.Since(s.started)) / float64(s.pulled) * float64(left) 1114 log.Info("Syncing beacon headers", "downloaded", s.pulled, "left", left, "eta", common.PrettyDuration(eta)) 1115 } 1116 } 1117 return linked, merged 1118 } 1119 1120 // cleanStales removes previously synced beacon headers that have become stale 1121 // due to the downloader backfilling past the tracked tail. 1122 func (s *skeleton) cleanStales(filled *types.Header) error { 1123 number := filled.Number.Uint64() 1124 log.Trace("Cleaning stale beacon headers", "filled", number, "hash", filled.Hash()) 1125 1126 // If the filled header is below the linked subchain, something's corrupted 1127 // internally. Report and error and refuse to do anything. 1128 if number+1 < s.progress.Subchains[0].Tail { 1129 return fmt.Errorf("filled header below beacon header tail: %d < %d", number, s.progress.Subchains[0].Tail) 1130 } 1131 // If nothing in subchain is filled, don't bother to do cleanup. 1132 if number+1 == s.progress.Subchains[0].Tail { 1133 return nil 1134 } 1135 // If the latest fill was on a different subchain, it means the backfiller 1136 // was interrupted before it got to do any meaningful work, no cleanup 1137 header := rawdb.ReadSkeletonHeader(s.db, filled.Number.Uint64()) 1138 if header == nil { 1139 log.Debug("Filled header outside of skeleton range", "number", number, "head", s.progress.Subchains[0].Head, "tail", s.progress.Subchains[0].Tail) 1140 return nil 1141 } else if header.Hash() != filled.Hash() { 1142 log.Debug("Filled header on different sidechain", "number", number, "filled", filled.Hash(), "skeleton", header.Hash()) 1143 return nil 1144 } 1145 var ( 1146 start uint64 1147 end uint64 1148 batch = s.db.NewBatch() 1149 ) 1150 if number < s.progress.Subchains[0].Head { 1151 // The skeleton chain is partially consumed, set the new tail as filled+1. 1152 tail := rawdb.ReadSkeletonHeader(s.db, number+1) 1153 if tail.ParentHash != filled.Hash() { 1154 return fmt.Errorf("filled header is discontinuous with subchain: %d %s, please file an issue", number, filled.Hash()) 1155 } 1156 start, end = s.progress.Subchains[0].Tail, number+1 // remove headers in [tail, filled] 1157 s.progress.Subchains[0].Tail = tail.Number.Uint64() 1158 s.progress.Subchains[0].Next = tail.ParentHash 1159 } else { 1160 // The skeleton chain is fully consumed, set both head and tail as filled. 1161 start, end = s.progress.Subchains[0].Tail, filled.Number.Uint64() // remove headers in [tail, filled) 1162 s.progress.Subchains[0].Tail = filled.Number.Uint64() 1163 s.progress.Subchains[0].Next = filled.ParentHash 1164 1165 // If more headers were filled than available, push the entire subchain 1166 // forward to keep tracking the node's block imports. 1167 if number > s.progress.Subchains[0].Head { 1168 end = s.progress.Subchains[0].Head + 1 // delete the entire original range, including the head 1169 s.progress.Subchains[0].Head = number // assign a new head (tail is already assigned to this) 1170 1171 // The entire original skeleton chain was deleted and a new one 1172 // defined. Make sure the new single-header chain gets pushed to 1173 // disk to keep internal state consistent. 1174 rawdb.WriteSkeletonHeader(batch, filled) 1175 } 1176 } 1177 // Execute the trimming and the potential rewiring of the progress 1178 s.saveSyncStatus(batch) 1179 for n := start; n < end; n++ { 1180 // If the batch grew too big, flush it and continue with a new batch. 1181 // The catch is that the sync metadata needs to reflect the actually 1182 // flushed state, so temporarily change the subchain progress and 1183 // revert after the flush. 1184 if batch.ValueSize() >= ethdb.IdealBatchSize { 1185 tmpTail := s.progress.Subchains[0].Tail 1186 tmpNext := s.progress.Subchains[0].Next 1187 1188 s.progress.Subchains[0].Tail = n 1189 s.progress.Subchains[0].Next = rawdb.ReadSkeletonHeader(s.db, n).ParentHash 1190 s.saveSyncStatus(batch) 1191 1192 if err := batch.Write(); err != nil { 1193 log.Crit("Failed to write beacon trim data", "err", err) 1194 } 1195 batch.Reset() 1196 1197 s.progress.Subchains[0].Tail = tmpTail 1198 s.progress.Subchains[0].Next = tmpNext 1199 s.saveSyncStatus(batch) 1200 } 1201 rawdb.DeleteSkeletonHeader(batch, n) 1202 } 1203 if err := batch.Write(); err != nil { 1204 log.Crit("Failed to write beacon trim data", "err", err) 1205 } 1206 return nil 1207 } 1208 1209 // Bounds retrieves the current head and tail tracked by the skeleton syncer 1210 // and optionally the last known finalized header if any was announced and if 1211 // it is still in the sync range. This method is used by the backfiller, whose 1212 // life cycle is controlled by the skeleton syncer. 1213 // 1214 // Note, the method will not use the internal state of the skeleton, but will 1215 // rather blindly pull stuff from the database. This is fine, because the back- 1216 // filler will only run when the skeleton chain is fully downloaded and stable. 1217 // There might be new heads appended, but those are atomic from the perspective 1218 // of this method. Any head reorg will first tear down the backfiller and only 1219 // then make the modification. 1220 func (s *skeleton) Bounds() (head *types.Header, tail *types.Header, final *types.Header, err error) { 1221 // Read the current sync progress from disk and figure out the current head. 1222 // Although there's a lot of error handling here, these are mostly as sanity 1223 // checks to avoid crashing if a programming error happens. These should not 1224 // happen in live code. 1225 status := rawdb.ReadSkeletonSyncStatus(s.db) 1226 if len(status) == 0 { 1227 return nil, nil, nil, errors.New("beacon sync not yet started") 1228 } 1229 progress := new(skeletonProgress) 1230 if err := json.Unmarshal(status, progress); err != nil { 1231 return nil, nil, nil, err 1232 } 1233 head = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Head) 1234 if head == nil { 1235 return nil, nil, nil, fmt.Errorf("head skeleton header %d is missing", progress.Subchains[0].Head) 1236 } 1237 tail = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Tail) 1238 if tail == nil { 1239 return nil, nil, nil, fmt.Errorf("tail skeleton header %d is missing", progress.Subchains[0].Tail) 1240 } 1241 if progress.Finalized != nil && tail.Number.Uint64() <= *progress.Finalized && *progress.Finalized <= head.Number.Uint64() { 1242 final = rawdb.ReadSkeletonHeader(s.db, *progress.Finalized) 1243 if final == nil { 1244 return nil, nil, nil, fmt.Errorf("finalized skeleton header %d is missing", *progress.Finalized) 1245 } 1246 } 1247 return head, tail, final, nil 1248 } 1249 1250 // Header retrieves a specific header tracked by the skeleton syncer. This method 1251 // is meant to be used by the backfiller, whose life cycle is controlled by the 1252 // skeleton syncer. 1253 // 1254 // Note, outside the permitted runtimes, this method might return nil results and 1255 // subsequent calls might return headers from different chains. 1256 func (s *skeleton) Header(number uint64) *types.Header { 1257 return rawdb.ReadSkeletonHeader(s.db, number) 1258 }