# Copyright (c) 2018-2022 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. when (NimMajor, NimMinor) < (1, 4): {.push raises: [Defect].} else: {.push raises: [].} import stew/base10, chronicles, chronos, ./sync/sync_manager, ./consensus_object_pools/blockchain_dag, ./spec/eth2_apis/rest_beacon_client, ./spec/[beaconstate, eth2_merkleization, forks, presets, state_transition], "."/[beacon_clock, beacon_chain_db] type DbCache = object summaries: Table[Eth2Digest, BeaconBlockSummary] slots: seq[Option[Eth2Digest]] proc updateSlots(cache: var DbCache, root: Eth2Digest, slot: Slot) = # The slots mapping stores one linear block history - we construct it by # starting from a given root/slot and walking the known parents as far back # as possible which ensures that all blocks belong to the same history if cache.slots.len() < slot.int + 1: cache.slots.setLen(slot.int + 1) var root = root lastSlot = slot while true: cache.summaries.withValue(root, v) do: let slot = v[].slot for i in slot.int + 1..= dbCache.slots.lenu64(): error "Downloaded block does not match checkpoint history" quit 1 if not dbCache.slots[childSlot.int].isSome(): # Should never happen - we download slots backwards error "Downloaded block does not match checkpoint history" quit 1 let knownRoot = dbCache.slots[childSlot.int].get() if knownRoot == ZERO_HASH: childSlot += 1 continue dbCache.summaries.withValue(knownRoot, summary): if summary[].parent_root != blck.root: error "Downloaded block does not match checkpoint history", blockRoot = shortLog(blck.root), expectedRoot = shortLog(summary[].parent_root) quit 1 break # This shouldn't happen - we should have downloaded the child and # updated knownBlocks before here error "Expected child block not found in checkpoint history" quit 1 if blck.root notin dbCache.summaries: db.putBlock(blck.asTrusted()) dbCache.update(blck) let newStamp = SyncMoment.now(processed) if newStamp.stamp - stamp.stamp > 12.seconds: syncCount += 1 let remaining = blck.message.slot.int slotsPerSec = speed(stamp, newStamp) avgSyncSpeed = avgSyncSpeed + (slotsPerSec - avgSyncSpeed) / float(syncCount) info "Backfilling", timeleft = toTimeLeftString( if avgSyncSpeed >= 0.001: Duration.fromFloatSeconds(remaining.float / avgSyncSpeed) else: InfiniteDuration), slotsPerSecond = avgSyncSpeed, remainingSlots = remaining stamp = newStamp # Download blocks backwards from the checkpoint slot, skipping the ones we # already have in the database. We'll do a few downloads in parallel which # risks having some redundant downloads going on, but speeds things up try: for i in 0'u64..<(checkpointSlot.uint64 + gets.lenu64()): if not isNil(gets[int(i mod gets.lenu64)]): await processBlock( gets[int(i mod gets.lenu64)], checkpointSlot + gets.lenu64() - uint64(i)) gets[int(i mod gets.lenu64)] = nil if i < checkpointSlot: let slot = checkpointSlot - i if dbCache.isKnown(slot): continue gets[int(i mod gets.lenu64)] = downloadBlock(slot) if i mod 1024 == 0: db.checkpoint() # Transfer stuff from wal periodically true except CatchableError as exc: # Block download failed notice "Backfilling incomplete - blocks will be downloaded when starting the node", msg = exc.msg false else: notice "Database initialized, historical blocks will be backfilled when starting the node", missingSlots false if reindex and canReindex: notice "Reindexing historical state lookup tables (you can interrupt this process at any time)" # Build a DAG let validatorMonitor = newClone(ValidatorMonitor.init(false, false)) dag = ChainDAGRef.init(cfg, db, validatorMonitor, {}) dag.rebuildIndex() notice "Done, your beacon node is ready to serve you! Don't forget to check that you're on the canonical chain by comparing the checkpoint root with other online sources. See https://nimbus.guide/trusted-node-sync.html for more information.", checkpointRoot when isMainModule: import std/[os], networking/network_metadata let backfill = os.paramCount() > 4 and os.paramStr(5) == "true" waitFor doTrustedNodeSync( getRuntimeConfig(some os.paramStr(1)), os.paramStr(2), os.paramStr(3), os.paramStr(4), backfill)