diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index c6c238fd5a3..6dcdcafd89d 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -35,9 +35,11 @@ use std::net::TcpListener; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; +use std::time::{SystemTime, UNIX_EPOCH}; use timer::spawn_timer; use tokio::sync::oneshot; use types::{ + consts::deneb::MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, test_utils::generate_deterministic_keypairs, BeaconState, ChainSpec, EthSpec, ExecutionBlockHash, Hash256, SignedBeaconBlock, }; @@ -45,6 +47,11 @@ use types::{ /// Interval between polling the eth1 node for genesis information. pub const ETH1_GENESIS_UPDATE_INTERVAL_MILLIS: u64 = 7_000; +/// Reduces the blob availability period by some epochs. Helps prevent the user +/// from starting a genesis sync so near to the blob pruning window that blobs +/// have been pruned before they can managed to sync the chain. +const BLOB_AVAILABILITY_REDUCTION_EPOCHS: u64 = 2; + /// Builds a `Client` instance. /// /// ## Notes @@ -252,6 +259,37 @@ where let genesis_state = genesis_state(&runtime_context, &config, log).await?; + // If the user has not explicitly allowed genesis sync, prevent + // the user from trying to sync from genesis if we're outside of + // the blob P2P availability window. + // + // It doesn't make sense to try and sync the chain if we can't + // verify blob availability by downloading blobs from the P2P + // network. The user should do a checkpoint sync instead. + if !config.allow_insecure_genesis_sync { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| format!("Unable to read system time: {e:}"))? + .as_secs(); + let genesis_time = genesis_state.genesis_time(); + let blob_availability_window = MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS.as_u64() + * TEthSpec::slots_per_epoch() + * spec.seconds_per_slot; + // Shrink the blob availability window so users don't start + // a sync right before blobs start to disappear from the P2P + // network. + let reduced_blob_availability_window = blob_availability_window.saturating_sub(TEthSpec::slots_per_epoch() * spec.seconds_per_slot * BLOB_AVAILABILITY_REDUCTION_EPOCHS) + + if now > genesis_time + reduced_blob_availability_window { + return Err( + "Syncing from genesis is insecure and incompatible with data availability checks. \ + You should instead perform a checkpoint sync from a trusted node using the --checkpoint-sync-url option. \ + For a list of public endpoints, see:\nhttps://eth-clients.github.io/checkpoint-sync-endpoints/" + .to_string(), + ); + } + } + builder.genesis_state(genesis_state).map(|v| (v, None))? } ClientGenesis::WeakSubjSszBytes { diff --git a/beacon_node/client/src/config.rs b/beacon_node/client/src/config.rs index 20afdb948bb..275f9998640 100644 --- a/beacon_node/client/src/config.rs +++ b/beacon_node/client/src/config.rs @@ -78,6 +78,7 @@ pub struct Config { pub beacon_processor: BeaconProcessorConfig, pub genesis_state_url: Option, pub genesis_state_url_timeout: Duration, + pub allow_insecure_genesis_sync: bool, } impl Default for Config { @@ -108,6 +109,7 @@ impl Default for Config { genesis_state_url: <_>::default(), // This default value should always be overwritten by the CLI default value. genesis_state_url_timeout: Duration::from_secs(60), + allow_insecure_genesis_sync: false, } } } diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index f2f49bb56bf..32333b4f7be 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -958,7 +958,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { .arg( Arg::with_name("allow-insecure-genesis-sync") .long("allow-insecure-genesis-sync") - .help("Enable syncing from genesis. This is insecure after Capella due to long-range attacks. This should only be used for testing. DO NOT use on mainnet!") + .help("Enable syncing from genesis, which is generally insecure and incompatible with data availability checks. \ + Checkpoint syncing is the preferred method for syncing a node. \ + Only use this flag when testing. DO NOT use on mainnet!") .conflicts_with("checkpoint-sync-url") .conflicts_with("checkpoint-state") .takes_value(false) diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 4493b155d5e..7463456faa4 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -488,6 +488,8 @@ pub fn get_config( None }; + client_config.allow_insecure_genesis_sync = cli_args.is_present("allow-insecure-genesis-sync"); + client_config.genesis = if eth2_network_config.genesis_state_is_known() { // Set up weak subjectivity sync, or start from the hardcoded genesis state. if let (Some(initial_state_path), Some(initial_block_path)) = ( @@ -518,15 +520,8 @@ pub fn get_config( .map_err(|e| format!("Invalid checkpoint sync URL: {:?}", e))?; ClientGenesis::CheckpointSyncUrl { url } - } else if cli_args.is_present("allow-insecure-genesis-sync") { - ClientGenesis::GenesisState } else { - return Err( - "Syncing from genesis is not secure post-Capella! \ - You should instead perform a checkpoint sync from a trusted node using the --checkpoint-sync-url option. \ - For a list of public endpoints, see:\nhttps://eth-clients.github.io/checkpoint-sync-endpoints/" - .to_string(), - ); + ClientGenesis::GenesisState } } else { if cli_args.is_present("checkpoint-state") || cli_args.is_present("checkpoint-sync-url") { diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index f51c26724fd..8d4c509f7d0 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -50,12 +50,6 @@ struct CommandLineTest { } impl CommandLineTest { fn new() -> CommandLineTest { - let mut base_cmd = base_cmd(); - base_cmd.arg("--allow-insecure-genesis-sync"); - CommandLineTest { cmd: base_cmd } - } - - fn without_allow_genesis_sync() -> CommandLineTest { let base_cmd = base_cmd(); CommandLineTest { cmd: base_cmd } }