2023-01-20 14:14:37 +00:00
# Copyright (c) 2018-2023 Status Research & Development GmbH
2022-01-17 09:27:08 +00:00
# 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.
2023-01-20 14:14:37 +00:00
{. push raises : [ ] . }
2022-01-17 09:27:08 +00:00
import
2022-12-07 10:24:51 +00:00
stew / [ base10 , results ] ,
chronicles , chronos , eth / async_utils ,
2023-06-12 12:22:32 +00:00
. / sync / [ light_client_sync_helpers , sync_manager ] ,
2022-11-10 10:44:47 +00:00
. / consensus_object_pools / [ block_clearance , blockchain_dag ] ,
2022-01-17 09:27:08 +00:00
. / spec / eth2_apis / rest_beacon_client ,
2023-04-17 17:18:54 +00:00
. / spec / [ beaconstate , eth2_merkleization , forks , light_client_sync ,
network , presets ,
2022-12-07 10:24:51 +00:00
state_transition , deposit_snapshots ] ,
2022-11-10 10:44:47 +00:00
" . " / [ beacon_clock , beacon_chain_db , era_db ]
2022-01-17 09:27:08 +00:00
2022-12-07 10:24:51 +00:00
from presto import RestDecodingError
const
largeRequestsTimeout = 60 . seconds # Downloading large items such as states.
smallRequestsTimeout = 30 . seconds # Downloading smaller items such as blocks and deposit snapshots.
proc fetchDepositSnapshot ( client : RestClientRef ) :
Future [ Result [ DepositTreeSnapshot , string ] ] {. async . } =
let resp = try :
awaitWithTimeout ( client . getDepositSnapshot ( ) , smallRequestsTimeout ) :
return err " Fetching /eth/v1/beacon/deposit_snapshot timed out "
except CatchableError as e :
return err ( " The trusted node likely does not support the /eth/v1/beacon/deposit_snapshot end-point: " & e . msg )
let data = resp . data . data
let snapshot = DepositTreeSnapshot (
eth1Block : data . execution_block_hash ,
depositContractState : DepositContractState (
branch : data . finalized ,
deposit_count : depositCountBytes ( data . deposit_count ) ) ,
blockHeight : data . execution_block_height )
if not snapshot . isValid ( data . deposit_root ) :
return err " The obtained deposit snapshot contains self-contradictory data "
return ok snapshot
2023-03-09 00:34:17 +00:00
from . / spec / datatypes / deneb import asSigVerified , shortLog
2022-12-14 17:30:56 +00:00
2023-04-16 06:07:07 +00:00
type
TrustedNodeSyncKind * {. pure . } = enum
TrustedBlockRoot ,
StateId
TrustedNodeSyncTarget * = object
case kind * : TrustedNodeSyncKind
of TrustedNodeSyncKind . TrustedBlockRoot :
trustedBlockRoot * : Eth2Digest
of TrustedNodeSyncKind . StateId :
stateId * : string
func shortLog * ( v : TrustedNodeSyncTarget ) : auto =
case v . kind
of TrustedNodeSyncKind . TrustedBlockRoot :
" trustedBlockRoot( " & $ v . trustedBlockRoot & " ) "
of TrustedNodeSyncKind . StateId :
v . stateId
chronicles . formatIt ( TrustedNodeSyncTarget ) : shortLog ( it )
2022-01-17 09:27:08 +00:00
proc doTrustedNodeSync * (
2022-12-07 10:24:51 +00:00
cfg : RuntimeConfig ,
databaseDir : string ,
eraDir : string ,
restUrl : string ,
2023-04-16 06:07:07 +00:00
syncTarget : TrustedNodeSyncTarget ,
2022-12-07 10:24:51 +00:00
backfill : bool ,
reindex : bool ,
downloadDepositSnapshot : bool ,
2022-01-17 09:27:08 +00:00
genesisState : ref ForkedHashedBeaconState = nil ) {. async . } =
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
logScope :
restUrl
2023-04-16 06:07:07 +00:00
syncTarget
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
2022-01-17 09:27:08 +00:00
notice " Starting trusted node sync " ,
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
databaseDir , backfill , reindex
2022-01-17 09:27:08 +00:00
2022-11-10 10:44:47 +00:00
var
client = RestClientRef . new ( restUrl ) . valueOr :
error " Cannot connect to server " , error = error
quit 1
2022-01-17 09:27:08 +00:00
let
2023-01-09 18:42:10 +00:00
db = BeaconChainDB . new ( databaseDir , cfg , inMemory = false )
2022-03-18 11:32:20 +00:00
defer :
db . close ( )
2022-01-17 09:27:08 +00:00
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
# If possible, we'll store the genesis state in the database - this is not
# strictly necessary but renders the resulting database compatible with
# versions prior to 22.11 and makes reindexing possible
let genesisState =
if ( let genesisRoot = db . getGenesisBlock ( ) ; genesisRoot . isSome ( ) ) :
let
genesisBlock = db . getForkedBlock ( genesisRoot . get ( ) ) . valueOr :
error " Cannot load genesis block from database " ,
genesisRoot = genesisRoot . get ( )
2022-03-11 12:49:47 +00:00
quit 1
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
genesisStateRoot = getForkedBlockField ( genesisBlock , state_root )
2023-03-11 00:35:52 +00:00
consensusFork = cfg . consensusForkAtEpoch ( GENESIS_EPOCH )
2022-01-17 09:27:08 +00:00
2023-03-11 00:35:52 +00:00
tmp = ( ref ForkedHashedBeaconState ) ( kind : consensusFork )
if not db . getState ( consensusFork , genesisStateRoot , tmp [ ] , noRollback ) :
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
error " Cannot load genesis state from database " ,
genesisStateRoot
quit 1
if ( genesisState ! = nil ) and
( getStateRoot ( tmp [ ] ) ! = getStateRoot ( genesisState [ ] ) ) :
error " Unexpected genesis state in database, is this the same network? " ,
databaseRoot = getStateRoot ( tmp [ ] ) ,
genesisRoot = getStateRoot ( genesisState [ ] )
quit 1
tmp
else :
let tmp = if genesisState ! = nil :
genesisState
else :
2023-06-09 20:11:14 +00:00
case syncTarget . kind
of TrustedNodeSyncKind . TrustedBlockRoot :
error " Genesis state is required when using `trustedBlockRoot` " ,
missingNetworkMetadataFile = " genesis.ssz "
# `genesis_time` and `genesis_validators_root` are required to check
# light client data signatures. They are not part of `config.yaml`.
# We could download the initial state based on `LightClientBootstrap`
# `state_root`, but that state is unlikely to be readily available and
# can be much larger than the genesis state. Furthermore, the major
# known networks bundle the `genesis.ssz` file with network metadata,
# so adding this complexity doesn't solve a practical usecase. Users
# on private networks may obtain a trusted `genesis.ssz` file and mark
# it as trusted by moving it into the network metadata folder.
#
# Note that `historical_roots` / `historical_summaries` may be used to
# prove correctness of a particular genesis state. However, there is
# currently no endpoint to obtain proofs, and they change for every
# slot, making it tricky to actually provide them.
quit 1
of TrustedNodeSyncKind . StateId :
notice " Downloading genesis state " , restUrl
try :
awaitWithTimeout (
client . getStateV2 ( StateIdent . init ( StateIdentType . Genesis ) , cfg ) ,
largeRequestsTimeout ) :
info " Attempt to download genesis state timed out "
2023-06-28 17:21:43 +00:00
# https://github.com/nim-lang/Nim/issues/22180
( ref ForkedHashedBeaconState ) ( nil )
2023-06-09 20:11:14 +00:00
except CatchableError as exc :
info " Unable to download genesis state " ,
error = exc . msg , restUrl
2022-12-07 10:24:51 +00:00
nil
2022-01-17 09:27:08 +00:00
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
if isNil ( tmp ) :
notice " Server is missing genesis state, node will not be able to reindex history " ,
restUrl
tmp
2022-01-17 09:27:08 +00:00
2022-11-10 10:44:47 +00:00
let
dbHead = db . getHeadBlock ( )
head = if dbHead . isSome ( ) :
let
bid = db . getBlockId ( dbHead . get ( ) ) . valueOr :
error " Database missing head block summary - database too old or corrupt " ,
headRoot = dbHead . get ( )
quit 1
Opt . some bid
else :
# When we don't have a head, we'll use the given checkpoint as head
Opt . none ( BlockId )
if head . isNone :
2023-04-16 06:07:07 +00:00
var stateRoot : Opt [ Eth2Digest ]
let stateId =
case syncTarget . kind
of TrustedNodeSyncKind . TrustedBlockRoot :
2023-06-24 15:43:30 +00:00
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.0/specs/altair/light-client/light-client.md#light-client-sync-process
2023-04-16 06:07:07 +00:00
const lcDataFork = LightClientDataFork . high
var bestViableCheckpoint : Opt [ tuple [ slot : Slot , state_root : Eth2Digest ] ]
func trackBestViableCheckpoint ( store : lcDataFork . LightClientStore ) =
if store . finalized_header . beacon . slot . is_epoch :
bestViableCheckpoint . ok ( (
slot : store . finalized_header . beacon . slot ,
state_root : store . finalized_header . beacon . state_root ) )
2023-06-09 20:11:14 +00:00
doAssert genesisState ! = nil , " Already checked for `TrustedBlockRoot` "
2023-04-16 06:07:07 +00:00
let
beaconClock = BeaconClock . init (
getStateField ( genesisState [ ] , genesis_time ) )
getBeaconTime = beaconClock . getBeaconTimeFn ( )
genesis_validators_root =
getStateField ( genesisState [ ] , genesis_validators_root )
forkDigests = newClone ForkDigests . init ( cfg , genesis_validators_root )
trustedBlockRoot = syncTarget . trustedBlockRoot
var bootstrap =
try :
notice " Downloading LC bootstrap " , trustedBlockRoot
awaitWithTimeout (
client . getLightClientBootstrap (
trustedBlockRoot , cfg , forkDigests ) ,
smallRequestsTimeout
) :
error " Attempt to download LC bootstrap timed out "
quit 1
except CatchableError as exc :
error " Unable to download LC bootstrap " , error = exc . msg
quit 1
if bootstrap . kind = = LightClientDataFork . None :
error " LC bootstrap unavailable on server "
quit 1
bootstrap . migrateToDataFork ( lcDataFork )
var storeRes =
initialize_light_client_store (
trustedBlockRoot , bootstrap . forky ( lcDataFork ) , cfg )
if storeRes . isErr :
error " `initialize_light_client_store` failed " , err = storeRes . error
quit 1
template store : auto = storeRes . get
store . trackBestViableCheckpoint ( )
while true :
let
finalized =
store . finalized_header . beacon . slot . sync_committee_period
optimistic =
store . optimistic_header . beacon . slot . sync_committee_period
current =
getBeaconTime ( ) . slotOrZero ( ) . sync_committee_period
isNextSyncCommitteeKnown =
store . is_next_sync_committee_known
let
periods : Slice [ SyncCommitteePeriod ] =
if finalized = = optimistic and not isNextSyncCommitteeKnown :
if finalized > = current :
finalized .. finalized
else :
finalized .. < current
elif finalized + 1 < current :
finalized + 1 .. < current
else :
break
startPeriod = periods . a
lastPeriod = periods . b
count = min ( periods . len , MAX_REQUEST_LIGHT_CLIENT_UPDATES ) . uint64
var updates =
try :
notice " Downloading LC updates " , startPeriod , count
awaitWithTimeout (
client . getLightClientUpdatesByRange (
startPeriod , count , cfg , forkDigests ) ,
smallRequestsTimeout
) :
error " Attempt to download LC updates timed out "
quit 1
except CatchableError as exc :
error " Unable to download LC updates " , error = exc . msg
quit 1
2023-06-12 12:22:32 +00:00
let e = updates . checkLightClientUpdates ( startPeriod , count )
if e . isErr :
error " Malformed LC updates response " , resError = e . error
2023-04-16 06:07:07 +00:00
quit 1
if updates . len = = 0 :
warn " Server does not appear to be fully synced "
break
for i in 0 .. < updates . len :
doAssert updates [ i ] . kind > LightClientDataFork . None
updates [ i ] . migrateToDataFork ( lcDataFork )
let res = process_light_client_update (
store , updates [ i ] . forky ( lcDataFork ) ,
getBeaconTime ( ) . slotOrZero ( ) , cfg , genesis_validators_root )
if not res . isOk :
error " `process_light_client_update` failed " , resError = res . error
quit 1
store . trackBestViableCheckpoint ( )
var finalityUpdate =
try :
notice " Downloading LC finality update "
awaitWithTimeout (
client . getLightClientFinalityUpdate ( cfg , forkDigests ) ,
smallRequestsTimeout
) :
error " Attempt to download LC finality update timed out "
quit 1
except CatchableError as exc :
error " Unable to download LC finality update " , error = exc . msg
quit 1
if bootstrap . kind = = LightClientDataFork . None :
error " LC finality update unavailable on server "
quit 1
finalityUpdate . migrateToDataFork ( lcDataFork )
let res = process_light_client_update (
store , finalityUpdate . forky ( lcDataFork ) ,
getBeaconTime ( ) . slotOrZero ( ) , cfg , genesis_validators_root )
if not res . isOk :
error " `process_light_client_update` failed " , resError = res . error
quit 1
store . trackBestViableCheckpoint ( )
if bestViableCheckpoint . isErr :
error " CP not on epoch boundary. Retry later " ,
latestCheckpointSlot = store . finalized_header . beacon . slot
quit 1
if not store . finalized_header . beacon . slot . is_epoch :
warn " CP not on epoch boundary. Using older one " ,
latestCheckpointSlot = store . finalized_header . beacon . slot ,
bestViableCheckpointSlot = bestViableCheckpoint . get . slot
stateRoot . ok bestViableCheckpoint . get . state_root
Base10 . toString ( distinctBase ( bestViableCheckpoint . get . slot ) )
of TrustedNodeSyncKind . StateId :
syncTarget . stateId
logScope : stateId
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
notice " Downloading checkpoint state "
2022-01-17 09:27:08 +00:00
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
let
state = try :
let id = block :
let tmp = StateIdent . decodeString ( stateId ) . valueOr :
error " Cannot decode checkpoint state id, must be a slot, hash, ' finalized ' or ' head ' "
quit 1
if tmp . kind = = StateQueryKind . Slot and not tmp . slot . is_epoch ( ) :
notice " Rounding given slot to epoch "
StateIdent . init ( tmp . slot . epoch ( ) . start_slot )
else :
tmp
2022-12-07 10:24:51 +00:00
awaitWithTimeout ( client . getStateV2 ( id , cfg ) , largeRequestsTimeout ) :
error " Attempt to download checkpoint state timed out "
quit 1
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
except CatchableError as exc :
error " Unable to download checkpoint state " ,
error = exc . msg
2022-03-11 12:49:47 +00:00
quit 1
2022-01-17 09:27:08 +00:00
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
if state = = nil :
2023-04-16 06:07:07 +00:00
error " No state found a given checkpoint "
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
quit 1
2022-01-17 09:27:08 +00:00
2023-04-16 06:07:07 +00:00
if stateRoot . isSome :
if state [ ] . getStateRoot ( ) ! = stateRoot . get :
error " Checkpoint state has incorrect root! " ,
expectedStateRoot = stateRoot . get ,
actualStateRoot = state [ ] . getStateRoot ( )
quit 1
info " Checkpoint state validated against LC data " ,
stateRoot = stateRoot . get
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
if not getStateField ( state [ ] , slot ) . is_epoch ( ) :
error " State slot must fall on an epoch boundary " ,
slot = getStateField ( state [ ] , slot ) ,
offset = getStateField ( state [ ] , slot ) -
getStateField ( state [ ] , slot ) . epoch . start_slot
quit 1
2022-01-17 09:27:08 +00:00
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
if genesisState ! = nil :
2023-06-09 20:11:14 +00:00
if getStateField ( state [ ] , genesis_time ) ! =
getStateField ( genesisState [ ] , genesis_time ) :
error " Checkpoint state does not match genesis " ,
timeInCheckpoint = getStateField ( state [ ] , genesis_time ) ,
timeInGenesis = getStateField ( genesisState [ ] , genesis_time )
quit 1
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
if getStateField ( state [ ] , genesis_validators_root ) ! =
getStateField ( genesisState [ ] , genesis_validators_root ) :
error " Checkpoint state does not match genesis " ,
rootInCheckpoint = getStateField ( state [ ] , genesis_validators_root ) ,
rootInGenesis = getStateField ( genesisState [ ] , genesis_validators_root )
2022-01-17 09:27:08 +00:00
quit 1
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
ChainDAGRef . preInit ( db , genesisState [ ] )
2022-01-17 09:27:08 +00:00
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
if getStateField ( genesisState [ ] , slot ) ! = getStateField ( state [ ] , slot ) :
ChainDAGRef . preInit ( db , state [ ] )
else :
ChainDAGRef . preInit ( db , state [ ] )
2022-12-07 10:24:51 +00:00
if downloadDepositSnapshot :
# Fetch deposit snapshot. This API endpoint is still optional.
let depositSnapshot = await fetchDepositSnapshot ( client )
if depositSnapshot . isOk :
if depositSnapshot . get . matches ( getStateField ( state [ ] , eth1_data ) ) :
info " Writing deposit contracts snapshot " ,
depositRoot = depositSnapshot . get . getDepositRoot ( ) ,
depositCount = depositSnapshot . get . getDepositCountU64
db . putDepositTreeSnapshot ( depositSnapshot . get )
else :
warn " The downloaded deposit snapshot does not agree with the downloaded state "
else :
warn " Deposit tree snapshot was not imported " , reason = depositSnapshot . error
2022-03-11 12:49:47 +00:00
else :
2022-09-29 20:55:18 +00:00
notice " Skipping checkpoint download, database already exists (remove db directory to get a fresh snapshot) " ,
2022-11-10 10:44:47 +00:00
databaseDir , head = shortLog ( head . get ( ) )
2022-01-17 09:27:08 +00:00
# Coming this far, we've done what ChainDAGRef.preInit would normally do -
2022-11-10 10:44:47 +00:00
# we can now load a ChainDAG to start backfilling it
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
let
2022-11-10 10:44:47 +00:00
validatorMonitor = newClone ( ValidatorMonitor . init ( false , false ) )
dag = ChainDAGRef . init ( cfg , db , validatorMonitor , { } , eraPath = eraDir )
backfillSlot = dag . backfill . slot
2022-12-23 07:42:55 +00:00
horizon = max ( dag . horizon , dag . frontfill . valueOr ( BlockId ( ) ) . slot )
2022-11-10 10:44:47 +00:00
2022-12-23 07:42:55 +00:00
let canReindex = if backfillSlot < = horizon :
info " Database backfilled " , backfill = dag . backfill , horizon
2022-03-11 12:49:47 +00:00
true
2022-01-17 09:27:08 +00:00
elif backfill :
2022-11-10 10:44:47 +00:00
# +1 because we need to download the frontfill slot for the frontfill match
# detection to kick in, in addBackfillBlock
2022-12-23 07:42:55 +00:00
let missingSlots = dag . backfill . slot - horizon + 1
2022-11-10 10:44:47 +00:00
2022-01-17 09:27:08 +00:00
notice " Downloading historical blocks - you can interrupt this process at any time and it automatically be completed when you start the beacon node " ,
2022-12-23 07:42:55 +00:00
backfillSlot , horizon , missingSlots
2022-01-17 09:27:08 +00:00
var # Same averaging as SyncManager
syncCount = 0
processed = 0 'u64
avgSyncSpeed = 0 .0
stamp = SyncMoment . now ( 0 )
2022-11-10 10:44:47 +00:00
proc downloadBlock ( slot : Slot ) :
Future [ Option [ ref ForkedSignedBeaconBlock ] ] {. async . } =
# Download block at given slot, retrying a few times,
var lastError : ref CatchableError
for i in 0 .. < 3 :
try :
2022-12-07 10:24:51 +00:00
return awaitWithTimeout ( client . getBlockV2 ( BlockIdent . init ( slot ) , cfg ) ,
smallRequestsTimeout ) :
raise newException ( CatchableError , " Request timed out " )
2022-11-10 10:44:47 +00:00
except RestResponseError as exc :
lastError = exc
notice " Server does not support block downloads / backfilling - blocks will be downloaded later " ,
msg = exc . msg
break
except CatchableError as exc :
# We'll assume this may be a connectivity error or something similar
lastError = exc
2022-01-17 09:27:08 +00:00
2022-11-10 10:44:47 +00:00
warn " Retrying download of block " , slot , err = exc . msg
client = RestClientRef . new ( restUrl ) . valueOr :
error " Cannot connect to server " , url = restUrl , error = error
2022-01-17 09:27:08 +00:00
quit 1
2022-11-10 10:44:47 +00:00
raise lastError
2022-01-17 09:27:08 +00:00
2022-11-10 10:44:47 +00:00
# Download several blocks in parallel but process them serially
proc processBlock ( blck : Option [ ref ForkedSignedBeaconBlock ] ) =
let newStamp = SyncMoment . now ( processed )
if newStamp . stamp - stamp . stamp > 12 . seconds :
syncCount + = 1
let
2022-12-23 07:42:55 +00:00
remaining = dag . backfill . slot - horizon
2022-11-10 10:44:47 +00:00
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
2022-01-17 09:27:08 +00:00
2022-11-10 10:44:47 +00:00
processed + = 1
if blck . isSome ( ) :
let
data = blck . get ( )
withBlck ( data [ ] ) :
2023-04-26 11:37:27 +00:00
let res =
case syncTarget . kind
of TrustedNodeSyncKind . TrustedBlockRoot :
# Trust-minimized sync: the server is only trusted for
# data availability, responses must be verified
dag . addBackfillBlock ( blck )
of TrustedNodeSyncKind . StateId :
# The server is fully trusted to provide accurate data;
# it could have provided a malicious state
dag . addBackfillBlock ( blck . asSigVerified ( ) )
if res . isErr ( ) :
2022-11-10 10:44:47 +00:00
case res . error ( )
2022-11-10 17:40:27 +00:00
of VerifierError . Invalid ,
VerifierError . MissingParent ,
VerifierError . UnviableFork :
2022-11-10 10:44:47 +00:00
error " Got invalid block from trusted node - is it on the right network? " ,
blck = shortLog ( blck ) , err = res . error ( )
quit 1
2022-11-10 17:40:27 +00:00
of VerifierError . Duplicate :
2022-11-10 10:44:47 +00:00
discard
2022-01-17 09:27:08 +00:00
2022-11-10 10:44:47 +00:00
# Download blocks backwards from the backfill slot, ie the first slot for
# which we don't have a block, when walking backwards from the head
try :
var
gets : array [ 16 , Future [ Option [ ref ForkedSignedBeaconBlock ] ] ]
2022-01-17 09:27:08 +00:00
2022-11-10 10:44:47 +00:00
for i in 0 . uint64 .. missingSlots + gets . lenu64 :
if i > = gets . lenu64 ( ) :
2022-01-17 09:27:08 +00:00
let
2022-11-10 10:44:47 +00:00
fut = gets [ int ( i mod gets . lenu64 ) ]
processBlock ( await fut )
2022-09-29 20:55:18 +00:00
2022-11-10 10:44:47 +00:00
if i < = backfillSlot :
let slot = backfillSlot - i
2022-09-29 20:55:18 +00:00
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
2022-01-17 09:27:08 +00:00
else :
2022-12-23 07:42:55 +00:00
let missingSlots = dag . backfill . slot - horizon
2022-01-17 09:27:08 +00:00
notice " Database initialized, historical blocks will be backfilled when starting the node " ,
2022-12-23 07:42:55 +00:00
missingSlots , backfill = dag . backfill , horizon
2022-01-17 09:27:08 +00:00
2022-03-11 12:49:47 +00:00
false
if reindex and canReindex :
notice " Reindexing historical state lookup tables (you can interrupt this process at any time) "
# Build a DAG
dag . rebuildIndex ( )
2022-08-01 00:32:08 +00:00
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. " ,
2022-11-10 10:44:47 +00:00
checkpoint = dag . head
2022-01-17 09:27:08 +00:00
when isMainModule :
2022-08-18 16:20:50 +00:00
import
std / [ os ] ,
networking / network_metadata
limit by-root requests to non-finalized blocks (#3293)
* limit by-root requests to non-finalized blocks
Presently, we keep a mapping from block root to `BlockRef` in memory -
this has simplified reasoning about the dag, but is not sustainable with
the chain growing.
We can distinguish between two cases where by-root access is useful:
* unfinalized blocks - this is where the beacon chain is operating
generally, by validating incoming data as interesting for future fork
choice decisions - bounded by the length of the unfinalized period
* finalized blocks - historical access in the REST API etc - no bounds,
really
In this PR, we limit the by-root block index to the first use case:
finalized chain data can more efficiently be addressed by slot number.
Future work includes:
* limiting the `BlockRef` horizon in general - each instance is 40
bytes+overhead which adds up - this needs further refactoring to deal
with the tail vs state problem
* persisting the finalized slot-to-hash index - this one also keeps
growing unbounded (albeit slowly)
Anyway, this PR easily shaves ~128mb of memory usage at the time of
writing.
* No longer honor `BeaconBlocksByRoot` requests outside of the
non-finalized period - previously, Nimbus would generously return any
block through this libp2p request - per the spec, finalized blocks
should be fetched via `BeaconBlocksByRange` instead.
* return `Opt[BlockRef]` instead of `nil` when blocks can't be found -
this becomes a lot more common now and thus deserves more attention
* `dag.blocks` -> `dag.forkBlocks` - this index only carries unfinalized
blocks from now - `finalizedBlocks` covers the other `BlockRef`
instances
* in backfill, verify that the last backfilled block leads back to
genesis, or panic
* add backfill timings to log
* fix missing check that `BlockRef` block can be fetched with
`getForkedBlock` reliably
* shortcut doppelganger check when feature is not enabled
* in REST/JSON-RPC, fetch blocks without involving `BlockRef`
* fix dag.blocks ref
2022-01-21 11:33:16 +00:00
2023-04-16 06:07:07 +00:00
let
syncTarget = TrustedNodeSyncTarget (
kind : TrustedNodeSyncKind . StateId ,
stateId : os . paramStr ( 5 ) )
backfill = os . paramCount ( ) > 5 and os . paramStr ( 6 ) = = " true "
2022-01-17 09:27:08 +00:00
waitFor doTrustedNodeSync (
State-only checkpoint state startup (#4251)
Currently, we require genesis and a checkpoint block and state to start
from an arbitrary slot - this PR relaxes this requirement so that we can
start with a state alone.
The current trusted-node-sync algorithm works by first downloading
blocks until we find an epoch aligned non-empty slot, then downloads the
state via slot.
However, current
[proposals](https://github.com/ethereum/beacon-APIs/pull/226) for
checkpointing prefer finalized state as
the main reference - this allows more simple access control and caching
on the server side - in particular, this should help checkpoint-syncing
from sources that have a fast `finalized` state download (like infura
and teku) but are slow when accessing state via slot.
Earlier versions of Nimbus will not be able to read databases created
without a checkpoint block and genesis. In most cases, backfilling makes
the database compatible except where genesis is also missing (custom
networks).
* backfill checkpoint block from libp2p instead of checkpoint source,
when doing trusted node sync
* allow starting the client without genesis / checkpoint block
* perform epoch start slot lookahead when loading tail state, so as to
deal with the case where the epoch start slot does not have a block
* replace `--blockId` with `--state-id` in TNS command line
* when replaying, also look at the parent of the last-known-block (even
if we don't have the parent block data, we can still replay from a
"parent" state) - in particular, this clears the way for implementing
state pruning
* deprecate `--finalized-checkpoint-block` option (no longer needed)
2022-11-02 10:02:38 +00:00
getRuntimeConfig ( some os . paramStr ( 1 ) ) , os . paramStr ( 2 ) , os . paramStr ( 3 ) ,
2023-04-16 06:07:07 +00:00
os . paramStr ( 4 ) , syncTarget , backfill , false , true )