allow `--external-beacon-api-url` to fallback to genesis if viable (#5998)
When using `--external-beacon-api-url`, one has to accompany it with either `--trusted-block-root` or `--trusted-state-root`. If neither is specified, we can fallback to a deeply finalized noncontroversial block root. For networks that started post Altair, e.g., Holesky, the genesis block root fulfills that requirement, as in, it is implicitly trusted. Therefore, if only `--external-beacon-api-url` is provided without any `--trusted-block-root` or `--trusted-state-root`, use genesis block root if it is a viable starting point (post-Altair). ``` build/nimbus_beacon_node \ --network=holesky \ --data-dir="$HOME/Downloads/nimbus/data/holesky" \ "--external-beacon-api-url=http://unstable.holesky.beacon-api.nimbus.team" \ --tcp-port=9010 --udp-port=9010 \ --rest --log-level=DEBUG \ --no-el ```
This commit is contained in:
parent
2c924bcc8c
commit
89d9dc24bd
|
@ -54,6 +54,43 @@ declareGauge next_action_wait,
|
||||||
declareCounter db_checkpoint_seconds,
|
declareCounter db_checkpoint_seconds,
|
||||||
"Time spent checkpointing the database to clear the WAL file"
|
"Time spent checkpointing the database to clear the WAL file"
|
||||||
|
|
||||||
|
proc fetchGenesisState(
|
||||||
|
config: BeaconNodeConf,
|
||||||
|
metadata: Eth2NetworkMetadata
|
||||||
|
): Future[ref ForkedHashedBeaconState] {.async: (raises: []).} =
|
||||||
|
let genesisBytes =
|
||||||
|
if metadata.genesis.kind != BakedIn and config.genesisState.isSome:
|
||||||
|
let res = io2.readAllBytes(config.genesisState.get.string)
|
||||||
|
res.valueOr:
|
||||||
|
error "Failed to read genesis state file", err = res.error.ioErrorMsg
|
||||||
|
quit 1
|
||||||
|
elif metadata.hasGenesis:
|
||||||
|
try:
|
||||||
|
if metadata.genesis.kind == BakedInUrl:
|
||||||
|
info "Obtaining genesis state",
|
||||||
|
sourceUrl = $config.genesisStateUrl
|
||||||
|
.get(parseUri metadata.genesis.url)
|
||||||
|
await metadata.fetchGenesisBytes(config.genesisStateUrl)
|
||||||
|
except CatchableError as err:
|
||||||
|
error "Failed to obtain genesis state",
|
||||||
|
source = metadata.genesis.sourceDesc,
|
||||||
|
err = err.msg
|
||||||
|
quit 1
|
||||||
|
else:
|
||||||
|
@[]
|
||||||
|
|
||||||
|
if genesisBytes.len > 0:
|
||||||
|
try:
|
||||||
|
newClone readSszForkedHashedBeaconState(metadata.cfg, genesisBytes)
|
||||||
|
except CatchableError as err:
|
||||||
|
error "Invalid genesis state",
|
||||||
|
size = genesisBytes.len,
|
||||||
|
digest = eth2digest(genesisBytes),
|
||||||
|
err = err.msg
|
||||||
|
quit 1
|
||||||
|
else:
|
||||||
|
nil
|
||||||
|
|
||||||
proc doRunTrustedNodeSync(
|
proc doRunTrustedNodeSync(
|
||||||
db: BeaconChainDB,
|
db: BeaconChainDB,
|
||||||
metadata: Eth2NetworkMetadata,
|
metadata: Eth2NetworkMetadata,
|
||||||
|
@ -64,10 +101,9 @@ proc doRunTrustedNodeSync(
|
||||||
trustedBlockRoot: Option[Eth2Digest],
|
trustedBlockRoot: Option[Eth2Digest],
|
||||||
backfill: bool,
|
backfill: bool,
|
||||||
reindex: bool,
|
reindex: bool,
|
||||||
downloadDepositSnapshot: bool) {.async.} =
|
downloadDepositSnapshot: bool,
|
||||||
let
|
genesisState: ref ForkedHashedBeaconState) {.async.} =
|
||||||
cfg = metadata.cfg
|
let syncTarget =
|
||||||
syncTarget =
|
|
||||||
if stateId.isSome:
|
if stateId.isSome:
|
||||||
if trustedBlockRoot.isSome:
|
if trustedBlockRoot.isSome:
|
||||||
warn "Ignoring `trustedBlockRoot`, `stateId` is set",
|
warn "Ignoring `trustedBlockRoot`, `stateId` is set",
|
||||||
|
@ -83,19 +119,9 @@ proc doRunTrustedNodeSync(
|
||||||
TrustedNodeSyncTarget(
|
TrustedNodeSyncTarget(
|
||||||
kind: TrustedNodeSyncKind.StateId,
|
kind: TrustedNodeSyncKind.StateId,
|
||||||
stateId: "finalized")
|
stateId: "finalized")
|
||||||
genesis =
|
|
||||||
if metadata.hasGenesis:
|
|
||||||
let genesisBytes = try: await metadata.fetchGenesisBytes()
|
|
||||||
except CatchableError as err:
|
|
||||||
error "Failed to obtain genesis state",
|
|
||||||
source = metadata.genesis.sourceDesc,
|
|
||||||
err = err.msg
|
|
||||||
quit 1
|
|
||||||
newClone(readSszForkedHashedBeaconState(cfg, genesisBytes))
|
|
||||||
else: nil
|
|
||||||
|
|
||||||
await db.doTrustedNodeSync(
|
await db.doTrustedNodeSync(
|
||||||
cfg,
|
metadata.cfg,
|
||||||
databaseDir,
|
databaseDir,
|
||||||
eraDir,
|
eraDir,
|
||||||
restUrl,
|
restUrl,
|
||||||
|
@ -103,7 +129,7 @@ proc doRunTrustedNodeSync(
|
||||||
backfill,
|
backfill,
|
||||||
reindex,
|
reindex,
|
||||||
downloadDepositSnapshot,
|
downloadDepositSnapshot,
|
||||||
genesis)
|
genesisState)
|
||||||
|
|
||||||
func getVanityLogs(stdoutKind: StdoutLogKind): VanityLogs =
|
func getVanityLogs(stdoutKind: StdoutLogKind): VanityLogs =
|
||||||
case stdoutKind
|
case stdoutKind
|
||||||
|
@ -544,13 +570,36 @@ proc init*(T: type BeaconNode,
|
||||||
db = BeaconChainDB.new(config.databaseDir, cfg, inMemory = false)
|
db = BeaconChainDB.new(config.databaseDir, cfg, inMemory = false)
|
||||||
|
|
||||||
if config.externalBeaconApiUrl.isSome and ChainDAGRef.isInitialized(db).isErr:
|
if config.externalBeaconApiUrl.isSome and ChainDAGRef.isInitialized(db).isErr:
|
||||||
if config.trustedStateRoot.isNone and config.trustedBlockRoot.isNone:
|
var genesisState: ref ForkedHashedBeaconState
|
||||||
|
let trustedBlockRoot =
|
||||||
|
if config.trustedStateRoot.isSome or config.trustedBlockRoot.isSome:
|
||||||
|
config.trustedBlockRoot
|
||||||
|
elif cfg.ALTAIR_FORK_EPOCH == GENESIS_EPOCH:
|
||||||
|
# Sync can be bootstrapped from the genesis block root
|
||||||
|
genesisState = await fetchGenesisState(config, metadata)
|
||||||
|
if genesisState != nil:
|
||||||
|
let genesisBlockRoot = get_initial_beacon_block(genesisState[]).root
|
||||||
|
notice "Neither `--trusted-block-root` nor `--trusted-state-root` " &
|
||||||
|
"provided with `--external-beacon-api-url`, " &
|
||||||
|
"falling back to genesis block root",
|
||||||
|
externalBeaconApiUrl = config.externalBeaconApiUrl.get,
|
||||||
|
trustedBlockRoot = config.trustedBlockRoot,
|
||||||
|
trustedStateRoot = config.trustedStateRoot,
|
||||||
|
genesisBlockRoot = $genesisBlockRoot
|
||||||
|
some genesisBlockRoot
|
||||||
|
else:
|
||||||
|
none[Eth2Digest]()
|
||||||
|
else:
|
||||||
|
none[Eth2Digest]()
|
||||||
|
if config.trustedStateRoot.isNone and trustedBlockRoot.isNone:
|
||||||
warn "Ignoring `--external-beacon-api-url`, neither " &
|
warn "Ignoring `--external-beacon-api-url`, neither " &
|
||||||
"`--trusted-block-root` nor `--trusted-state-root` are provided",
|
"`--trusted-block-root` nor `--trusted-state-root` provided",
|
||||||
externalBeaconApiUrl = config.externalBeaconApiUrl.get,
|
externalBeaconApiUrl = config.externalBeaconApiUrl.get,
|
||||||
trustedBlockRoot = config.trustedBlockRoot,
|
trustedBlockRoot = config.trustedBlockRoot,
|
||||||
trustedStateRoot = config.trustedStateRoot
|
trustedStateRoot = config.trustedStateRoot
|
||||||
else:
|
else:
|
||||||
|
if genesisState == nil:
|
||||||
|
genesisState = await fetchGenesisState(config, metadata)
|
||||||
await db.doRunTrustedNodeSync(
|
await db.doRunTrustedNodeSync(
|
||||||
metadata,
|
metadata,
|
||||||
config.databaseDir,
|
config.databaseDir,
|
||||||
|
@ -558,10 +607,11 @@ proc init*(T: type BeaconNode,
|
||||||
config.externalBeaconApiUrl.get,
|
config.externalBeaconApiUrl.get,
|
||||||
config.trustedStateRoot.map do (x: Eth2Digest) -> string:
|
config.trustedStateRoot.map do (x: Eth2Digest) -> string:
|
||||||
"0x" & x.data.toHex,
|
"0x" & x.data.toHex,
|
||||||
config.trustedBlockRoot,
|
trustedBlockRoot,
|
||||||
backfill = false,
|
backfill = false,
|
||||||
reindex = false,
|
reindex = false,
|
||||||
downloadDepositSnapshot = false)
|
downloadDepositSnapshot = false,
|
||||||
|
genesisState)
|
||||||
|
|
||||||
if config.finalizedCheckpointBlock.isSome:
|
if config.finalizedCheckpointBlock.isSome:
|
||||||
warn "--finalized-checkpoint-block has been deprecated, ignoring"
|
warn "--finalized-checkpoint-block has been deprecated, ignoring"
|
||||||
|
@ -609,40 +659,12 @@ proc init*(T: type BeaconNode,
|
||||||
var networkGenesisValidatorsRoot = metadata.bakedGenesisValidatorsRoot
|
var networkGenesisValidatorsRoot = metadata.bakedGenesisValidatorsRoot
|
||||||
|
|
||||||
if not ChainDAGRef.isInitialized(db).isOk():
|
if not ChainDAGRef.isInitialized(db).isOk():
|
||||||
let genesisState = if checkpointState != nil and getStateField(checkpointState[], slot) == 0:
|
let genesisState =
|
||||||
|
if checkpointState != nil and
|
||||||
|
getStateField(checkpointState[], slot) == 0:
|
||||||
checkpointState
|
checkpointState
|
||||||
else:
|
else:
|
||||||
let genesisBytes = block:
|
await fetchGenesisState(config, metadata)
|
||||||
if metadata.genesis.kind != BakedIn and config.genesisState.isSome:
|
|
||||||
let res = io2.readAllBytes(config.genesisState.get.string)
|
|
||||||
res.valueOr:
|
|
||||||
error "Failed to read genesis state file", err = res.error.ioErrorMsg
|
|
||||||
quit 1
|
|
||||||
elif metadata.hasGenesis:
|
|
||||||
try:
|
|
||||||
if metadata.genesis.kind == BakedInUrl:
|
|
||||||
info "Obtaining genesis state",
|
|
||||||
sourceUrl = $config.genesisStateUrl.get(parseUri metadata.genesis.url)
|
|
||||||
await metadata.fetchGenesisBytes(config.genesisStateUrl)
|
|
||||||
except CatchableError as err:
|
|
||||||
error "Failed to obtain genesis state",
|
|
||||||
source = metadata.genesis.sourceDesc,
|
|
||||||
err = err.msg
|
|
||||||
quit 1
|
|
||||||
else:
|
|
||||||
@[]
|
|
||||||
|
|
||||||
if genesisBytes.len > 0:
|
|
||||||
try:
|
|
||||||
newClone readSszForkedHashedBeaconState(cfg, genesisBytes)
|
|
||||||
except CatchableError as err:
|
|
||||||
error "Invalid genesis state",
|
|
||||||
size = genesisBytes.len,
|
|
||||||
digest = eth2digest(genesisBytes),
|
|
||||||
err = err.msg
|
|
||||||
quit 1
|
|
||||||
else:
|
|
||||||
nil
|
|
||||||
|
|
||||||
if genesisState == nil and checkpointState == nil:
|
if genesisState == nil and checkpointState == nil:
|
||||||
fatal "No database and no genesis snapshot found. Please supply a genesis.ssz " &
|
fatal "No database and no genesis snapshot found. Please supply a genesis.ssz " &
|
||||||
|
@ -2263,6 +2285,7 @@ proc handleStartUpCmd(config: var BeaconNodeConf) {.raises: [CatchableError].} =
|
||||||
let
|
let
|
||||||
metadata = loadEth2Network(config)
|
metadata = loadEth2Network(config)
|
||||||
db = BeaconChainDB.new(config.databaseDir, metadata.cfg, inMemory = false)
|
db = BeaconChainDB.new(config.databaseDir, metadata.cfg, inMemory = false)
|
||||||
|
genesisState = waitFor fetchGenesisState(config, metadata)
|
||||||
waitFor db.doRunTrustedNodeSync(
|
waitFor db.doRunTrustedNodeSync(
|
||||||
metadata,
|
metadata,
|
||||||
config.databaseDir,
|
config.databaseDir,
|
||||||
|
@ -2272,7 +2295,8 @@ proc handleStartUpCmd(config: var BeaconNodeConf) {.raises: [CatchableError].} =
|
||||||
config.lcTrustedBlockRoot,
|
config.lcTrustedBlockRoot,
|
||||||
config.backfillBlocks,
|
config.backfillBlocks,
|
||||||
config.reindex,
|
config.reindex,
|
||||||
config.downloadDepositSnapshot)
|
config.downloadDepositSnapshot,
|
||||||
|
genesisState)
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
{.pop.} # TODO moduletests exceptions
|
{.pop.} # TODO moduletests exceptions
|
||||||
|
|
Loading…
Reference in New Issue