Disallow genesis sync outside blob pruning window (#5038)

* Disallow Syncing From Genesis By Default

* Fix CLI Tests

* Perform checks in the `ClientBuilder`

* Tidy, fix tests

* Return an error based on the Deneb fork

* Fix typos

* Fix failing test

* Add missing CLI flag

* Fix CLI flags

* Add suggestion from Sean

* Fix conflict with blob sidecars epochs

---------

Co-authored-by: Mark Mackey <mark@sigmaprime.io>
This commit is contained in:
Paul Hauner 2024-01-10 06:17:01 +11:00 committed by GitHub
parent b47e3f252e
commit 12d3d237cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 0 deletions

View File

@ -35,6 +35,7 @@ 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::{
@ -45,6 +46,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 manage to sync the chain.
const BLOB_AVAILABILITY_REDUCTION_EPOCHS: u64 = 2;
/// Builds a `Client` instance.
///
/// ## Notes
@ -252,6 +258,45 @@ where
let genesis_state = genesis_state(&runtime_context, &config, log).await?;
// If the user has not explicitly allowed genesis sync, prevent
// them 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 {
if let Some(deneb_fork_epoch) = spec.deneb_fork_epoch {
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 deneb_time =
genesis_time + (deneb_fork_epoch.as_u64() * 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_p2p_availability_epochs = spec
.min_epochs_for_blob_sidecars_requests
.saturating_sub(BLOB_AVAILABILITY_REDUCTION_EPOCHS);
let blob_availability_window = reduced_p2p_availability_epochs
* TEthSpec::slots_per_epoch()
* spec.seconds_per_slot;
if now > deneb_time + 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: https://eth-clients.github.io/checkpoint-sync-endpoints/ \
Alternatively, use --allow-insecure-genesis-sync if the risks are understood."
.to_string(),
);
}
}
}
builder.genesis_state(genesis_state).map(|v| (v, None))?
}
ClientGenesis::WeakSubjSszBytes {

View File

@ -78,6 +78,7 @@ pub struct Config {
pub beacon_processor: BeaconProcessorConfig,
pub genesis_state_url: Option<String>,
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,
}
}
}

View File

@ -955,6 +955,16 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.takes_value(true)
.default_value("180")
)
.arg(
Arg::with_name("allow-insecure-genesis-sync")
.long("allow-insecure-genesis-sync")
.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)
)
.arg(
Arg::with_name("reconstruct-historic-states")
.long("reconstruct-historic-states")

View File

@ -497,6 +497,8 @@ pub fn get_config<E: EthSpec>(
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)) = (

View File

@ -9,6 +9,10 @@ USAGE:
lighthouse beacon_node [FLAGS] [OPTIONS]
FLAGS:
--allow-insecure-genesis-sync 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!
--always-prefer-builder-payload This flag is deprecated and has no effect.
--always-prepare-payload Send payload attributes with every fork choice update. This is intended
for use by block builders, relays and developers. You should set a fee

View File

@ -92,6 +92,22 @@ fn staking_flag() {
});
}
#[test]
fn allow_insecure_genesis_sync() {
CommandLineTest::new()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.allow_insecure_genesis_sync, false);
});
CommandLineTest::new()
.flag("allow-insecure-genesis-sync", None)
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.allow_insecure_genesis_sync, true);
});
}
#[test]
fn wss_checkpoint_flag() {
let state = Some(Checkpoint {