mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 04:37:13 +00:00
Resolve merge conflicts
This commit is contained in:
@@ -4,19 +4,12 @@ version = "0.2.0"
|
||||
authors = ["Sigma Prime <contact@sigmaprime.io>"]
|
||||
edition = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
operation_pool = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
state_processing = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
beacon_chain = { workspace = true }
|
||||
beacon_processor = { workspace = true }
|
||||
directory = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
environment = { workspace = true }
|
||||
eth1 = { workspace = true }
|
||||
eth2 = { workspace = true }
|
||||
eth2_config = { workspace = true }
|
||||
ethereum_ssz = { workspace = true }
|
||||
@@ -46,3 +39,9 @@ tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
types = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
operation_pool = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
state_processing = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
@@ -1,54 +1,52 @@
|
||||
use crate::Client;
|
||||
use crate::compute_light_client_updates::{
|
||||
compute_light_client_updates, LIGHT_CLIENT_SERVER_CHANNEL_CAPACITY,
|
||||
LIGHT_CLIENT_SERVER_CHANNEL_CAPACITY, compute_light_client_updates,
|
||||
};
|
||||
use crate::config::{ClientGenesis, Config as ClientConfig};
|
||||
use crate::notifier::spawn_notifier;
|
||||
use crate::Client;
|
||||
use beacon_chain::attestation_simulator::start_attestation_simulator_service;
|
||||
use beacon_chain::data_availability_checker::start_availability_cache_maintenance_service;
|
||||
use beacon_chain::graffiti_calculator::start_engine_version_cache_refresh_service;
|
||||
use beacon_chain::proposer_prep_service::start_proposer_prep_service;
|
||||
use beacon_chain::schema_change::migrate_schema;
|
||||
use beacon_chain::{
|
||||
BeaconChain, BeaconChainTypes, MigratorConfig, ServerSentEventHandler,
|
||||
builder::{BeaconChainBuilder, Witness},
|
||||
eth1_chain::{CachingEth1Backend, Eth1Chain},
|
||||
slot_clock::{SlotClock, SystemTimeSlotClock},
|
||||
state_advance_timer::spawn_state_advance_timer,
|
||||
store::{HotColdDB, ItemStore, StoreConfig},
|
||||
BeaconChain, BeaconChainTypes, Eth1ChainBackend, MigratorConfig, ServerSentEventHandler,
|
||||
};
|
||||
use beacon_chain::{Kzg, LightClientProducerEvent};
|
||||
use beacon_processor::{BeaconProcessor, BeaconProcessorChannels};
|
||||
use beacon_processor::{BeaconProcessorConfig, BeaconProcessorQueueLengths};
|
||||
use environment::RuntimeContext;
|
||||
use eth1::{Config as Eth1Config, Service as Eth1Service};
|
||||
use eth2::{
|
||||
types::{BlockId, StateId},
|
||||
BeaconNodeHttpClient, Error as ApiError, Timeouts,
|
||||
types::{BlockId, StateId},
|
||||
};
|
||||
use execution_layer::test_utils::generate_genesis_header;
|
||||
use execution_layer::ExecutionLayer;
|
||||
use execution_layer::test_utils::generate_genesis_header;
|
||||
use futures::channel::mpsc::Receiver;
|
||||
use genesis::{interop_genesis_state, Eth1GenesisService, DEFAULT_ETH1_BLOCK_HASH};
|
||||
use lighthouse_network::{prometheus_client::registry::Registry, NetworkGlobals};
|
||||
use genesis::{DEFAULT_ETH1_BLOCK_HASH, interop_genesis_state};
|
||||
use lighthouse_network::identity::Keypair;
|
||||
use lighthouse_network::{NetworkGlobals, prometheus_client::registry::Registry};
|
||||
use monitoring_api::{MonitoringHttpClient, ProcessType};
|
||||
use network::{NetworkConfig, NetworkSenders, NetworkService};
|
||||
use rand::rngs::{OsRng, StdRng};
|
||||
use rand::SeedableRng;
|
||||
use rand::rngs::{OsRng, StdRng};
|
||||
use slasher::Slasher;
|
||||
use slasher_service::SlasherService;
|
||||
use std::net::TcpListener;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use store::database::interface::BeaconNodeBackend;
|
||||
use timer::spawn_timer;
|
||||
use tokio::sync::oneshot;
|
||||
use tracing::{debug, info, warn};
|
||||
use tracing::{debug, info, instrument, warn};
|
||||
use types::data_column_custody_group::compute_ordered_custody_column_indices;
|
||||
use types::{
|
||||
test_utils::generate_deterministic_keypairs, BeaconState, BlobSidecarList, ChainSpec, EthSpec,
|
||||
ExecutionBlockHash, Hash256, SignedBeaconBlock,
|
||||
BeaconState, BlobSidecarList, ChainSpec, EthSpec, ExecutionBlockHash, Hash256,
|
||||
SignedBeaconBlock, test_utils::generate_deterministic_keypairs,
|
||||
};
|
||||
|
||||
/// Interval between polling the eth1 node for genesis information.
|
||||
@@ -80,7 +78,6 @@ pub struct ClientBuilder<T: BeaconChainTypes> {
|
||||
chain_spec: Option<Arc<ChainSpec>>,
|
||||
beacon_chain_builder: Option<BeaconChainBuilder<T>>,
|
||||
beacon_chain: Option<Arc<BeaconChain<T>>>,
|
||||
eth1_service: Option<Eth1Service>,
|
||||
network_globals: Option<Arc<NetworkGlobals<T::EthSpec>>>,
|
||||
network_senders: Option<NetworkSenders<T::EthSpec>>,
|
||||
libp2p_registry: Option<Registry>,
|
||||
@@ -95,11 +92,10 @@ pub struct ClientBuilder<T: BeaconChainTypes> {
|
||||
eth_spec_instance: T::EthSpec,
|
||||
}
|
||||
|
||||
impl<TSlotClock, TEth1Backend, E, THotStore, TColdStore>
|
||||
ClientBuilder<Witness<TSlotClock, TEth1Backend, E, THotStore, TColdStore>>
|
||||
impl<TSlotClock, E, THotStore, TColdStore>
|
||||
ClientBuilder<Witness<TSlotClock, E, THotStore, TColdStore>>
|
||||
where
|
||||
TSlotClock: SlotClock + Clone + 'static,
|
||||
TEth1Backend: Eth1ChainBackend<E> + 'static,
|
||||
E: EthSpec + 'static,
|
||||
THotStore: ItemStore<E> + 'static,
|
||||
TColdStore: ItemStore<E> + 'static,
|
||||
@@ -115,7 +111,6 @@ where
|
||||
chain_spec: None,
|
||||
beacon_chain_builder: None,
|
||||
beacon_chain: None,
|
||||
eth1_service: None,
|
||||
network_globals: None,
|
||||
network_senders: None,
|
||||
libp2p_registry: None,
|
||||
@@ -156,10 +151,12 @@ where
|
||||
|
||||
/// Initializes the `BeaconChainBuilder`. The `build_beacon_chain` method will need to be
|
||||
/// called later in order to actually instantiate the `BeaconChain`.
|
||||
#[instrument(skip_all)]
|
||||
pub async fn beacon_chain_builder(
|
||||
mut self,
|
||||
client_genesis: ClientGenesis,
|
||||
config: ClientConfig,
|
||||
node_id: [u8; 32],
|
||||
) -> Result<Self, String> {
|
||||
let store = self.store.clone();
|
||||
let chain_spec = self.chain_spec.clone();
|
||||
@@ -191,15 +188,17 @@ where
|
||||
};
|
||||
|
||||
let kzg_err_msg = |e| format!("Failed to load trusted setup: {:?}", e);
|
||||
let trusted_setup = config.trusted_setup.clone();
|
||||
let kzg = if spec.is_peer_das_scheduled() {
|
||||
Kzg::new_from_trusted_setup_das_enabled(trusted_setup).map_err(kzg_err_msg)?
|
||||
} else if spec.deneb_fork_epoch.is_some() {
|
||||
Kzg::new_from_trusted_setup(trusted_setup).map_err(kzg_err_msg)?
|
||||
Kzg::new_from_trusted_setup(&config.trusted_setup).map_err(kzg_err_msg)?
|
||||
} else {
|
||||
Kzg::new_from_trusted_setup_no_precomp(trusted_setup).map_err(kzg_err_msg)?
|
||||
Kzg::new_from_trusted_setup_no_precomp(&config.trusted_setup).map_err(kzg_err_msg)?
|
||||
};
|
||||
|
||||
let ordered_custody_column_indices =
|
||||
compute_ordered_custody_column_indices::<E>(node_id, &spec).map_err(|e| {
|
||||
format!("Failed to compute ordered custody column indices: {:?}", e)
|
||||
})?;
|
||||
|
||||
let builder = BeaconChainBuilder::new(eth_spec_instance, Arc::new(kzg))
|
||||
.store(store)
|
||||
.task_executor(context.executor.clone())
|
||||
@@ -211,10 +210,12 @@ where
|
||||
.beacon_graffiti(beacon_graffiti)
|
||||
.event_handler(event_handler)
|
||||
.execution_layer(execution_layer)
|
||||
.import_all_data_columns(config.network.subscribe_all_data_column_subnets)
|
||||
.node_custody_type(config.chain.node_custody_type)
|
||||
.ordered_custody_column_indices(ordered_custody_column_indices)
|
||||
.validator_monitor_config(config.validator_monitor.clone())
|
||||
.rng(Box::new(
|
||||
StdRng::from_rng(OsRng).map_err(|e| format!("Failed to create RNG: {:?}", e))?,
|
||||
StdRng::try_from_rng(&mut OsRng)
|
||||
.map_err(|e| format!("Failed to create RNG: {:?}", e))?,
|
||||
));
|
||||
|
||||
let builder = if let Some(slasher) = self.slasher.clone() {
|
||||
@@ -261,7 +262,7 @@ where
|
||||
client_genesis
|
||||
};
|
||||
|
||||
let (beacon_chain_builder, eth1_service_option) = match client_genesis {
|
||||
let beacon_chain_builder = match client_genesis {
|
||||
ClientGenesis::Interop {
|
||||
validator_count,
|
||||
genesis_time,
|
||||
@@ -274,7 +275,7 @@ where
|
||||
None,
|
||||
&spec,
|
||||
)?;
|
||||
builder.genesis_state(genesis_state).map(|v| (v, None))?
|
||||
builder.genesis_state(genesis_state)?
|
||||
}
|
||||
ClientGenesis::InteropMerge {
|
||||
validator_count,
|
||||
@@ -289,7 +290,7 @@ where
|
||||
execution_payload_header,
|
||||
&spec,
|
||||
)?;
|
||||
builder.genesis_state(genesis_state).map(|v| (v, None))?
|
||||
builder.genesis_state(genesis_state)?
|
||||
}
|
||||
ClientGenesis::GenesisState => {
|
||||
info!("Starting from known genesis state");
|
||||
@@ -303,41 +304,41 @@ where
|
||||
// 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()
|
||||
* E::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_p2p_availability_epochs = spec
|
||||
.min_epochs_for_blob_sidecars_requests
|
||||
.saturating_sub(BLOB_AVAILABILITY_REDUCTION_EPOCHS);
|
||||
let blob_availability_window = reduced_p2p_availability_epochs
|
||||
if !config.allow_insecure_genesis_sync
|
||||
&& 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()
|
||||
* E::slots_per_epoch()
|
||||
* spec.seconds_per_slot;
|
||||
* spec.seconds_per_slot);
|
||||
|
||||
if now > deneb_time + blob_availability_window {
|
||||
return Err(
|
||||
// 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
|
||||
* E::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))?
|
||||
builder.genesis_state(genesis_state)?
|
||||
}
|
||||
ClientGenesis::WeakSubjSszBytes {
|
||||
anchor_state_bytes,
|
||||
@@ -353,10 +354,11 @@ where
|
||||
.map_err(|e| format!("Unable to parse weak subj state SSZ: {:?}", e))?;
|
||||
let anchor_block = SignedBeaconBlock::from_ssz_bytes(&anchor_block_bytes, &spec)
|
||||
.map_err(|e| format!("Unable to parse weak subj block SSZ: {:?}", e))?;
|
||||
let anchor_blobs = if anchor_block.message().body().has_blobs() {
|
||||
|
||||
// Providing blobs is optional now and not providing them is recommended.
|
||||
// Backfill can handle downloading the blobs or columns for the checkpoint block.
|
||||
let anchor_blobs = if let Some(anchor_blobs_bytes) = anchor_blobs_bytes {
|
||||
let max_blobs_len = spec.max_blobs_per_block(anchor_block.epoch()) as usize;
|
||||
let anchor_blobs_bytes = anchor_blobs_bytes
|
||||
.ok_or("Blobs for checkpoint must be provided using --checkpoint-blobs")?;
|
||||
Some(
|
||||
BlobSidecarList::from_ssz_bytes(&anchor_blobs_bytes, max_blobs_len)
|
||||
.map_err(|e| format!("Unable to parse weak subj blobs SSZ: {e:?}"))?,
|
||||
@@ -366,14 +368,12 @@ where
|
||||
};
|
||||
let genesis_state = genesis_state(&runtime_context, &config).await?;
|
||||
|
||||
builder
|
||||
.weak_subjectivity_state(
|
||||
anchor_state,
|
||||
anchor_block,
|
||||
anchor_blobs,
|
||||
genesis_state,
|
||||
)
|
||||
.map(|v| (v, None))?
|
||||
builder.weak_subjectivity_state(
|
||||
anchor_state,
|
||||
anchor_block,
|
||||
anchor_blobs,
|
||||
genesis_state,
|
||||
)?
|
||||
}
|
||||
ClientGenesis::CheckpointSyncUrl { url } => {
|
||||
info!(
|
||||
@@ -391,47 +391,6 @@ where
|
||||
)),
|
||||
);
|
||||
|
||||
let deposit_snapshot = if config.sync_eth1_chain {
|
||||
// We want to fetch deposit snapshot before fetching the finalized beacon state to
|
||||
// ensure that the snapshot is not newer than the beacon state that satisfies the
|
||||
// deposit finalization conditions
|
||||
debug!("Downloading deposit snapshot");
|
||||
let deposit_snapshot_result = remote
|
||||
.get_deposit_snapshot()
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
ApiError::InvalidSsz(e) => format!(
|
||||
"Unable to parse SSZ: {:?}. Ensure the checkpoint-sync-url refers to a \
|
||||
node for the correct network",
|
||||
e
|
||||
),
|
||||
e => format!("Error fetching deposit snapshot from remote: {:?}", e),
|
||||
});
|
||||
match deposit_snapshot_result {
|
||||
Ok(Some(deposit_snapshot)) => {
|
||||
if deposit_snapshot.is_valid() {
|
||||
Some(deposit_snapshot)
|
||||
} else {
|
||||
warn!("Remote BN sent invalid deposit snapshot!");
|
||||
None
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
warn!("Remote BN does not support EIP-4881 fast deposit sync");
|
||||
None
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
error = e,
|
||||
"Remote BN does not support EIP-4881 fast deposit sync"
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
debug!("Downloading finalized state");
|
||||
let state = remote
|
||||
.get_debug_beacon_states_ssz::<E>(StateId::Finalized, &spec)
|
||||
@@ -460,10 +419,14 @@ where
|
||||
|
||||
debug!("Downloaded finalized block");
|
||||
|
||||
let blobs = if block.message().body().has_blobs() {
|
||||
// `get_blob_sidecars` API is deprecated from Fulu and may not be supported by all servers
|
||||
let is_before_fulu = !spec
|
||||
.fork_name_at_slot::<E>(finalized_block_slot)
|
||||
.fulu_enabled();
|
||||
let blobs = if is_before_fulu && block.message().body().has_blobs() {
|
||||
debug!("Downloading finalized blobs");
|
||||
if let Some(response) = remote
|
||||
.get_blobs::<E>(BlockId::Root(block_root), None, &spec)
|
||||
.get_blob_sidecars::<E>(BlockId::Root(block_root), None, &spec)
|
||||
.await
|
||||
.map_err(|e| format!("Error fetching finalized blobs from remote: {e:?}"))?
|
||||
{
|
||||
@@ -491,126 +454,24 @@ where
|
||||
"Loaded checkpoint block and state"
|
||||
);
|
||||
|
||||
let service =
|
||||
deposit_snapshot.and_then(|snapshot| match Eth1Service::from_deposit_snapshot(
|
||||
config.eth1,
|
||||
spec.clone(),
|
||||
&snapshot,
|
||||
) {
|
||||
Ok(service) => {
|
||||
info!(
|
||||
deposits_loaded = snapshot.deposit_count,
|
||||
"Loaded deposit tree snapshot"
|
||||
);
|
||||
Some(service)
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(error = ?e,
|
||||
"Unable to load deposit snapshot"
|
||||
);
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
builder
|
||||
.weak_subjectivity_state(state, block, blobs, genesis_state)
|
||||
.map(|v| (v, service))?
|
||||
builder.weak_subjectivity_state(state, block, blobs, genesis_state)?
|
||||
}
|
||||
ClientGenesis::DepositContract => {
|
||||
info!(
|
||||
eth1_endpoints = ?config.eth1.endpoint,
|
||||
contract_deploy_block = config.eth1.deposit_contract_deploy_block,
|
||||
deposit_contract = &config.eth1.deposit_contract_address,
|
||||
"Waiting for eth2 genesis from eth1"
|
||||
);
|
||||
|
||||
let genesis_service =
|
||||
Eth1GenesisService::new(config.eth1, context.eth2_config().spec.clone())?;
|
||||
|
||||
// If the HTTP API server is enabled, start an instance of it where it only
|
||||
// contains a reference to the eth1 service (all non-eth1 endpoints will fail
|
||||
// gracefully).
|
||||
//
|
||||
// Later in this function we will shutdown this temporary "waiting for genesis"
|
||||
// server so the real one can be started later.
|
||||
let (exit_tx, exit_rx) = oneshot::channel::<()>();
|
||||
let http_listen_opt = if self.http_api_config.enabled {
|
||||
#[allow(clippy::type_complexity)]
|
||||
let ctx: Arc<
|
||||
http_api::Context<
|
||||
Witness<TSlotClock, TEth1Backend, E, THotStore, TColdStore>,
|
||||
>,
|
||||
> = Arc::new(http_api::Context {
|
||||
config: self.http_api_config.clone(),
|
||||
chain: None,
|
||||
network_senders: None,
|
||||
network_globals: None,
|
||||
beacon_processor_send: None,
|
||||
beacon_processor_reprocess_send: None,
|
||||
eth1_service: Some(genesis_service.eth1_service.clone()),
|
||||
sse_logging_components: runtime_context.sse_logging_components.clone(),
|
||||
});
|
||||
|
||||
// Discard the error from the oneshot.
|
||||
let exit_future = async {
|
||||
let _ = exit_rx.await;
|
||||
};
|
||||
|
||||
let (listen_addr, server) = http_api::serve(ctx, exit_future)
|
||||
.map_err(|e| format!("Unable to start HTTP API server: {:?}", e))?;
|
||||
|
||||
let http_api_task = async move {
|
||||
server.await;
|
||||
debug!("HTTP API server task ended");
|
||||
};
|
||||
|
||||
context
|
||||
.clone()
|
||||
.executor
|
||||
.spawn_without_exit(http_api_task, "http-api");
|
||||
|
||||
Some(listen_addr)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let genesis_state = genesis_service
|
||||
.wait_for_genesis_state(Duration::from_millis(
|
||||
ETH1_GENESIS_UPDATE_INTERVAL_MILLIS,
|
||||
))
|
||||
.await?;
|
||||
|
||||
let _ = exit_tx.send(());
|
||||
|
||||
if let Some(http_listen) = http_listen_opt {
|
||||
// This is a bit of a hack to ensure that the HTTP server has indeed shutdown.
|
||||
//
|
||||
// We will restart it again after we've finished setting up for genesis.
|
||||
while TcpListener::bind(http_listen).is_err() {
|
||||
warn!(
|
||||
port = %http_listen,
|
||||
"Waiting for HTTP server port to open"
|
||||
);
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
|
||||
builder
|
||||
.genesis_state(genesis_state)
|
||||
.map(|v| (v, Some(genesis_service.into_core_service())))?
|
||||
return Err("Loading genesis from deposit contract no longer supported".to_string());
|
||||
}
|
||||
ClientGenesis::FromStore => builder.resume_from_db().map(|v| (v, None))?,
|
||||
ClientGenesis::FromStore => builder.resume_from_db()?,
|
||||
};
|
||||
|
||||
if config.sync_eth1_chain {
|
||||
self.eth1_service = eth1_service_option;
|
||||
}
|
||||
self.beacon_chain_builder = Some(beacon_chain_builder);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Starts the networking stack.
|
||||
pub async fn network(mut self, config: Arc<NetworkConfig>) -> Result<Self, String> {
|
||||
pub async fn network(
|
||||
mut self,
|
||||
config: Arc<NetworkConfig>,
|
||||
local_keypair: Keypair,
|
||||
) -> Result<Self, String> {
|
||||
let beacon_chain = self
|
||||
.beacon_chain
|
||||
.clone()
|
||||
@@ -633,12 +494,12 @@ where
|
||||
};
|
||||
|
||||
let (network_globals, network_senders) = NetworkService::start(
|
||||
beacon_chain,
|
||||
beacon_chain.clone(),
|
||||
config,
|
||||
context.executor,
|
||||
libp2p_registry.as_mut(),
|
||||
beacon_processor_channels.beacon_processor_tx.clone(),
|
||||
beacon_processor_channels.work_reprocessing_tx.clone(),
|
||||
local_keypair,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to start network: {:?}", e))?;
|
||||
@@ -753,9 +614,10 @@ where
|
||||
///
|
||||
/// If type inference errors are being raised, see the comment on the definition of `Self`.
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[instrument(name = "build_client", skip_all)]
|
||||
pub fn build(
|
||||
mut self,
|
||||
) -> Result<Client<Witness<TSlotClock, TEth1Backend, E, THotStore, TColdStore>>, String> {
|
||||
) -> Result<Client<Witness<TSlotClock, E, THotStore, TColdStore>>, String> {
|
||||
let runtime_context = self
|
||||
.runtime_context
|
||||
.as_ref()
|
||||
@@ -775,11 +637,7 @@ where
|
||||
chain: self.beacon_chain.clone(),
|
||||
network_senders: self.network_senders.clone(),
|
||||
network_globals: self.network_globals.clone(),
|
||||
eth1_service: self.eth1_service.clone(),
|
||||
beacon_processor_send: Some(beacon_processor_channels.beacon_processor_tx.clone()),
|
||||
beacon_processor_reprocess_send: Some(
|
||||
beacon_processor_channels.work_reprocessing_tx.clone(),
|
||||
),
|
||||
sse_logging_components: runtime_context.sse_logging_components.clone(),
|
||||
});
|
||||
|
||||
@@ -843,8 +701,6 @@ where
|
||||
}
|
||||
.spawn_manager(
|
||||
beacon_processor_channels.beacon_processor_rx,
|
||||
beacon_processor_channels.work_reprocessing_tx.clone(),
|
||||
beacon_processor_channels.work_reprocessing_rx,
|
||||
None,
|
||||
beacon_chain.slot_clock.clone(),
|
||||
beacon_chain.spec.maximum_gossip_clock_disparity(),
|
||||
@@ -918,7 +774,7 @@ where
|
||||
compute_light_client_updates(
|
||||
&inner_chain,
|
||||
light_client_server_rv,
|
||||
beacon_processor_channels.work_reprocessing_tx,
|
||||
beacon_processor_channels.beacon_processor_tx,
|
||||
)
|
||||
.await
|
||||
},
|
||||
@@ -950,16 +806,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSlotClock, TEth1Backend, E, THotStore, TColdStore>
|
||||
ClientBuilder<Witness<TSlotClock, TEth1Backend, E, THotStore, TColdStore>>
|
||||
impl<TSlotClock, E, THotStore, TColdStore>
|
||||
ClientBuilder<Witness<TSlotClock, E, THotStore, TColdStore>>
|
||||
where
|
||||
TSlotClock: SlotClock + Clone + 'static,
|
||||
TEth1Backend: Eth1ChainBackend<E> + 'static,
|
||||
E: EthSpec + 'static,
|
||||
THotStore: ItemStore<E> + 'static,
|
||||
TColdStore: ItemStore<E> + 'static,
|
||||
{
|
||||
/// Consumes the internal `BeaconChainBuilder`, attaching the resulting `BeaconChain` to self.
|
||||
#[instrument(skip_all)]
|
||||
pub fn build_beacon_chain(mut self) -> Result<Self, String> {
|
||||
let context = self
|
||||
.runtime_context
|
||||
@@ -987,11 +843,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSlotClock, TEth1Backend, E>
|
||||
ClientBuilder<Witness<TSlotClock, TEth1Backend, E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>
|
||||
impl<TSlotClock, E>
|
||||
ClientBuilder<Witness<TSlotClock, E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>
|
||||
where
|
||||
TSlotClock: SlotClock + 'static,
|
||||
TEth1Backend: Eth1ChainBackend<E> + 'static,
|
||||
E: EthSpec + 'static,
|
||||
{
|
||||
/// Specifies that the `Client` should use a `HotColdDB` database.
|
||||
@@ -1002,11 +857,6 @@ where
|
||||
blobs_path: &Path,
|
||||
config: StoreConfig,
|
||||
) -> Result<Self, String> {
|
||||
let context = self
|
||||
.runtime_context
|
||||
.as_ref()
|
||||
.ok_or("disk_store requires a log")?
|
||||
.service_context("freezer_db".into());
|
||||
let spec = self
|
||||
.chain_spec
|
||||
.clone()
|
||||
@@ -1015,22 +865,8 @@ where
|
||||
self.db_path = Some(hot_path.into());
|
||||
self.freezer_db_path = Some(cold_path.into());
|
||||
|
||||
// Optionally grab the genesis state root.
|
||||
// This will only be required if a DB upgrade to V22 is needed.
|
||||
let genesis_state_root = context
|
||||
.eth2_network_config
|
||||
.as_ref()
|
||||
.and_then(|config| config.genesis_state_root::<E>().transpose())
|
||||
.transpose()?;
|
||||
|
||||
let schema_upgrade = |db, from, to| {
|
||||
migrate_schema::<Witness<TSlotClock, TEth1Backend, _, _, _>>(
|
||||
db,
|
||||
genesis_state_root,
|
||||
from,
|
||||
to,
|
||||
)
|
||||
};
|
||||
let schema_upgrade =
|
||||
|db, from, to| migrate_schema::<Witness<TSlotClock, _, _, _>>(db, from, to);
|
||||
|
||||
let store = HotColdDB::open(
|
||||
hot_path,
|
||||
@@ -1046,102 +882,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSlotClock, E, THotStore, TColdStore>
|
||||
ClientBuilder<Witness<TSlotClock, CachingEth1Backend<E>, E, THotStore, TColdStore>>
|
||||
impl<E, THotStore, TColdStore> ClientBuilder<Witness<SystemTimeSlotClock, E, THotStore, TColdStore>>
|
||||
where
|
||||
TSlotClock: SlotClock + 'static,
|
||||
E: EthSpec + 'static,
|
||||
THotStore: ItemStore<E> + 'static,
|
||||
TColdStore: ItemStore<E> + 'static,
|
||||
{
|
||||
/// Specifies that the `BeaconChain` should cache eth1 blocks/logs from a remote eth1 node
|
||||
/// (e.g., Parity/Geth) and refer to that cache when collecting deposits or eth1 votes during
|
||||
/// block production.
|
||||
pub async fn caching_eth1_backend(mut self, config: Eth1Config) -> Result<Self, String> {
|
||||
let context = self
|
||||
.runtime_context
|
||||
.as_ref()
|
||||
.ok_or("caching_eth1_backend requires a runtime_context")?
|
||||
.service_context("deposit_contract_rpc".into());
|
||||
let beacon_chain_builder = self
|
||||
.beacon_chain_builder
|
||||
.ok_or("caching_eth1_backend requires a beacon_chain_builder")?;
|
||||
let spec = self
|
||||
.chain_spec
|
||||
.clone()
|
||||
.ok_or("caching_eth1_backend requires a chain spec")?;
|
||||
|
||||
let backend = if let Some(eth1_service_from_genesis) = self.eth1_service {
|
||||
eth1_service_from_genesis.update_config(config)?;
|
||||
|
||||
// This cache is not useful because it's first (earliest) block likely the block that
|
||||
// triggered genesis.
|
||||
//
|
||||
// In order to vote we need to be able to go back at least 2 * `ETH1_FOLLOW_DISTANCE`
|
||||
// from the genesis-triggering block. Presently the block cache does not support
|
||||
// importing blocks with decreasing block numbers, it only accepts them in increasing
|
||||
// order. If this turns out to be a bottleneck we can update the block cache to allow
|
||||
// adding earlier blocks too.
|
||||
eth1_service_from_genesis.drop_block_cache();
|
||||
|
||||
CachingEth1Backend::from_service(eth1_service_from_genesis)
|
||||
} else if config.purge_cache {
|
||||
CachingEth1Backend::new(config, spec)?
|
||||
} else {
|
||||
beacon_chain_builder
|
||||
.get_persisted_eth1_backend()?
|
||||
.map(|persisted| {
|
||||
Eth1Chain::from_ssz_container(&persisted, config.clone(), spec.clone())
|
||||
.map(|chain| chain.into_backend())
|
||||
})
|
||||
.unwrap_or_else(|| CachingEth1Backend::new(config, spec.clone()))?
|
||||
};
|
||||
|
||||
self.eth1_service = Some(backend.core.clone());
|
||||
|
||||
// Starts the service that connects to an eth1 node and periodically updates caches.
|
||||
backend.start(context.executor);
|
||||
|
||||
self.beacon_chain_builder = Some(beacon_chain_builder.eth1_backend(Some(backend)));
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Do not use any eth1 backend. The client will not be able to produce beacon blocks.
|
||||
pub fn no_eth1_backend(mut self) -> Result<Self, String> {
|
||||
let beacon_chain_builder = self
|
||||
.beacon_chain_builder
|
||||
.ok_or("caching_eth1_backend requires a beacon_chain_builder")?;
|
||||
|
||||
self.beacon_chain_builder = Some(beacon_chain_builder.no_eth1_backend());
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Use an eth1 backend that can produce blocks but is not connected to an Eth1 node.
|
||||
///
|
||||
/// This backend will never produce deposits so it's impossible to add validators after
|
||||
/// genesis. The `Eth1Data` votes will be deterministic junk data.
|
||||
///
|
||||
/// ## Notes
|
||||
///
|
||||
/// The client is given the `CachingEth1Backend` type, but the http backend is never started and the
|
||||
/// caches are never used.
|
||||
pub fn dummy_eth1_backend(mut self) -> Result<Self, String> {
|
||||
let beacon_chain_builder = self
|
||||
.beacon_chain_builder
|
||||
.ok_or("caching_eth1_backend requires a beacon_chain_builder")?;
|
||||
|
||||
self.beacon_chain_builder = Some(beacon_chain_builder.dummy_eth1_backend()?);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TEth1Backend, E, THotStore, TColdStore>
|
||||
ClientBuilder<Witness<SystemTimeSlotClock, TEth1Backend, E, THotStore, TColdStore>>
|
||||
where
|
||||
TEth1Backend: Eth1ChainBackend<E> + 'static,
|
||||
E: EthSpec + 'static,
|
||||
THotStore: ItemStore<E> + 'static,
|
||||
TColdStore: ItemStore<E> + 'static,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, LightClientProducerEvent};
|
||||
use beacon_processor::work_reprocessing_queue::ReprocessQueueMessage;
|
||||
use futures::channel::mpsc::Receiver;
|
||||
use beacon_processor::{BeaconProcessorSend, Work, WorkEvent};
|
||||
use futures::StreamExt;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tracing::error;
|
||||
use futures::channel::mpsc::Receiver;
|
||||
use tracing::{debug, error};
|
||||
|
||||
// Each `LightClientProducerEvent` is ~200 bytes. With the light_client server producing only recent
|
||||
// updates it is okay to drop some events in case of overloading. In normal network conditions
|
||||
@@ -14,7 +14,7 @@ pub(crate) const LIGHT_CLIENT_SERVER_CHANNEL_CAPACITY: usize = 32;
|
||||
pub async fn compute_light_client_updates<T: BeaconChainTypes>(
|
||||
chain: &BeaconChain<T>,
|
||||
mut light_client_server_rv: Receiver<LightClientProducerEvent<T::EthSpec>>,
|
||||
reprocess_tx: Sender<ReprocessQueueMessage>,
|
||||
beacon_processor_send: BeaconProcessorSend<T::EthSpec>,
|
||||
) {
|
||||
// Should only receive events for recent blocks, import_block filters by blocks close to clock.
|
||||
//
|
||||
@@ -27,11 +27,17 @@ pub async fn compute_light_client_updates<T: BeaconChainTypes>(
|
||||
chain
|
||||
.recompute_and_cache_light_client_updates(event)
|
||||
.unwrap_or_else(|e| {
|
||||
error!("error computing light_client updates {:?}", e);
|
||||
debug!("error computing light_client updates {:?}", e);
|
||||
});
|
||||
|
||||
let msg = ReprocessQueueMessage::NewLightClientOptimisticUpdate { parent_root };
|
||||
if reprocess_tx.try_send(msg).is_err() {
|
||||
if beacon_processor_send
|
||||
.try_send(WorkEvent {
|
||||
drop_during_sync: true,
|
||||
work: Work::Reprocess(msg),
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
error!(%parent_root,"Failed to inform light client update")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use beacon_chain::graffiti_calculator::GraffitiOrigin;
|
||||
use beacon_chain::validator_monitor::ValidatorMonitorConfig;
|
||||
use beacon_chain::TrustedSetup;
|
||||
use beacon_processor::BeaconProcessorConfig;
|
||||
use directory::DEFAULT_ROOT_DIR;
|
||||
use environment::LoggerConfig;
|
||||
@@ -59,7 +58,6 @@ pub struct Config {
|
||||
/// Path where the blobs database will be located if blobs should be in a separate database.
|
||||
pub blobs_db_path: Option<PathBuf>,
|
||||
pub log_file: PathBuf,
|
||||
pub sync_eth1_chain: bool,
|
||||
/// Graffiti to be inserted everytime we create a block if the validator doesn't specify.
|
||||
pub beacon_graffiti: GraffitiOrigin,
|
||||
pub validator_monitor: ValidatorMonitorConfig,
|
||||
@@ -70,9 +68,8 @@ pub struct Config {
|
||||
pub store: store::StoreConfig,
|
||||
pub network: network::NetworkConfig,
|
||||
pub chain: beacon_chain::ChainConfig,
|
||||
pub eth1: eth1::Config,
|
||||
pub execution_layer: Option<execution_layer::Config>,
|
||||
pub trusted_setup: TrustedSetup,
|
||||
pub trusted_setup: Vec<u8>,
|
||||
pub http_api: http_api::Config,
|
||||
pub http_metrics: http_metrics::Config,
|
||||
pub monitoring_api: Option<monitoring_api::Config>,
|
||||
@@ -86,9 +83,6 @@ pub struct Config {
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
let trusted_setup: TrustedSetup = serde_json::from_reader(get_trusted_setup().as_slice())
|
||||
.expect("Unable to read trusted setup file");
|
||||
|
||||
Self {
|
||||
data_dir: PathBuf::from(DEFAULT_ROOT_DIR),
|
||||
db_name: "chain_db".to_string(),
|
||||
@@ -99,10 +93,8 @@ impl Default for Config {
|
||||
store: <_>::default(),
|
||||
network: NetworkConfig::default(),
|
||||
chain: <_>::default(),
|
||||
sync_eth1_chain: true,
|
||||
eth1: <_>::default(),
|
||||
execution_layer: None,
|
||||
trusted_setup,
|
||||
trusted_setup: get_trusted_setup(),
|
||||
beacon_graffiti: GraffitiOrigin::default(),
|
||||
http_api: <_>::default(),
|
||||
http_metrics: <_>::default(),
|
||||
|
||||
@@ -10,7 +10,7 @@ use lighthouse_network::{Enr, Multiaddr, NetworkGlobals};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use beacon_chain::{BeaconChainTypes, Eth1ChainBackend};
|
||||
pub use beacon_chain::BeaconChainTypes;
|
||||
pub use builder::ClientBuilder;
|
||||
pub use config::{ClientGenesis, Config as ClientConfig};
|
||||
pub use eth2_config::Eth2Config;
|
||||
|
||||
@@ -11,7 +11,14 @@ pub static SYNC_SLOTS_PER_SECOND: LazyLock<Result<IntGauge>> = LazyLock::new(||
|
||||
pub static IS_SYNCED: LazyLock<Result<IntGauge>> = LazyLock::new(|| {
|
||||
try_create_int_gauge(
|
||||
"sync_eth2_synced",
|
||||
"Metric to check if the beacon chain is synced to head. 0 if not synced and non-zero if synced"
|
||||
"Metric to check if the beacon chain is synced to head. 0 if not synced and non-zero if synced",
|
||||
)
|
||||
});
|
||||
|
||||
pub static IS_OPTIMISTIC_SYNC: LazyLock<Result<IntGauge>> = LazyLock::new(|| {
|
||||
try_create_int_gauge(
|
||||
"optimistic_sync",
|
||||
"Metric to check if the beacon chain is in optimistic sync mode. 0 if synced and 1 if optimistic sync",
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
use crate::metrics;
|
||||
use beacon_chain::{
|
||||
bellatrix_readiness::{BellatrixReadiness, GenesisExecutionPayloadStatus, MergeConfig},
|
||||
capella_readiness::CapellaReadiness,
|
||||
deneb_readiness::DenebReadiness,
|
||||
eip7805_readiness::Eip7805Readiness,
|
||||
electra_readiness::ElectraReadiness,
|
||||
fulu_readiness::FuluReadiness,
|
||||
BeaconChain, BeaconChainTypes, ExecutionStatus,
|
||||
bellatrix_readiness::{
|
||||
BellatrixReadiness, GenesisExecutionPayloadStatus, MergeConfig, SECONDS_IN_A_WEEK,
|
||||
},
|
||||
};
|
||||
use lighthouse_network::{types::SyncState, NetworkGlobals};
|
||||
use execution_layer::{
|
||||
EngineCapabilities,
|
||||
http::{
|
||||
ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3, ENGINE_GET_PAYLOAD_V2,
|
||||
ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4, ENGINE_GET_PAYLOAD_V5, ENGINE_NEW_PAYLOAD_V2,
|
||||
ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V4,
|
||||
},
|
||||
};
|
||||
use lighthouse_network::{NetworkGlobals, types::SyncState};
|
||||
use logging::crit;
|
||||
use slot_clock::SlotClock;
|
||||
use std::sync::Arc;
|
||||
@@ -31,6 +36,9 @@ const SPEEDO_OBSERVATIONS: usize = 4;
|
||||
/// The number of slots between logs that give detail about backfill process.
|
||||
const BACKFILL_LOG_INTERVAL: u64 = 5;
|
||||
|
||||
pub const FORK_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK * 2;
|
||||
pub const ENGINE_CAPABILITIES_REFRESH_INTERVAL: u64 = 300;
|
||||
|
||||
/// Spawns a notifier service which periodically logs information about the node.
|
||||
pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
executor: task_executor::TaskExecutor,
|
||||
@@ -49,6 +57,9 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
// Store info if we are required to do a backfill sync.
|
||||
let original_oldest_block_slot = beacon_chain.store.get_anchor_info().oldest_block_slot;
|
||||
|
||||
// Use this info during custody backfill sync.
|
||||
let mut original_earliest_data_column_slot = None;
|
||||
|
||||
let interval_future = async move {
|
||||
// Perform pre-genesis logging.
|
||||
loop {
|
||||
@@ -61,9 +72,8 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
wait_time = estimated_time_pretty(Some(next_slot.as_secs() as f64)),
|
||||
"Waiting for genesis"
|
||||
);
|
||||
eth1_logging(&beacon_chain);
|
||||
bellatrix_readiness_logging(Slot::new(0), &beacon_chain).await;
|
||||
capella_readiness_logging(Slot::new(0), &beacon_chain).await;
|
||||
post_bellatrix_readiness_logging(Slot::new(0), &beacon_chain).await;
|
||||
genesis_execution_payload_logging(&beacon_chain).await;
|
||||
sleep(slot_duration).await;
|
||||
}
|
||||
@@ -73,6 +83,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
|
||||
// Perform post-genesis logging.
|
||||
let mut last_backfill_log_slot = None;
|
||||
let mut last_custody_backfill_log_slot = None;
|
||||
|
||||
loop {
|
||||
// Run the notifier half way through each slot.
|
||||
@@ -105,6 +116,18 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
let mut speedo = speedo.lock().await;
|
||||
speedo.clear();
|
||||
}
|
||||
(_, SyncState::CustodyBackFillSyncing { .. }) => {
|
||||
// We have transitioned to a custody backfill sync. Reset the speedo.
|
||||
let mut speedo = speedo.lock().await;
|
||||
last_custody_backfill_log_slot = None;
|
||||
speedo.clear();
|
||||
}
|
||||
(SyncState::CustodyBackFillSyncing { .. }, _) => {
|
||||
// We have transitioned from a custody backfill sync, reset the speedo
|
||||
let mut speedo = speedo.lock().await;
|
||||
last_custody_backfill_log_slot = None;
|
||||
speedo.clear();
|
||||
}
|
||||
(_, _) => {}
|
||||
}
|
||||
current_sync_state = sync_state;
|
||||
@@ -147,6 +170,38 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
Instant::now(),
|
||||
);
|
||||
}
|
||||
SyncState::CustodyBackFillSyncing { .. } => {
|
||||
match beacon_chain.store.get_data_column_custody_info() {
|
||||
Ok(data_column_custody_info) => {
|
||||
if let Some(earliest_data_column_slot) = data_column_custody_info
|
||||
.and_then(|info| info.earliest_data_column_slot)
|
||||
&& let Some(da_boundary) = beacon_chain.get_column_da_boundary()
|
||||
{
|
||||
sync_distance = earliest_data_column_slot.saturating_sub(
|
||||
da_boundary.start_slot(T::EthSpec::slots_per_epoch()),
|
||||
);
|
||||
|
||||
// We keep track of our starting point for custody backfill sync
|
||||
// so we can measure our speed of progress.
|
||||
if original_earliest_data_column_slot.is_none() {
|
||||
original_earliest_data_column_slot =
|
||||
Some(earliest_data_column_slot)
|
||||
}
|
||||
|
||||
if let Some(original_earliest_data_column_slot) =
|
||||
original_earliest_data_column_slot
|
||||
{
|
||||
speedo.observe(
|
||||
original_earliest_data_column_slot
|
||||
.saturating_sub(earliest_data_column_slot),
|
||||
Instant::now(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!(error=?e, "Unable to get data column custody info"),
|
||||
}
|
||||
}
|
||||
SyncState::SyncingFinalized { .. }
|
||||
| SyncState::SyncingHead { .. }
|
||||
| SyncState::SyncTransition => {
|
||||
@@ -183,6 +238,8 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
|
||||
// Log if we are backfilling.
|
||||
let is_backfilling = matches!(current_sync_state, SyncState::BackFillSyncing { .. });
|
||||
let is_custody_backfilling =
|
||||
matches!(current_sync_state, SyncState::CustodyBackFillSyncing { .. });
|
||||
if is_backfilling
|
||||
&& last_backfill_log_slot
|
||||
.is_none_or(|slot| slot + BACKFILL_LOG_INTERVAL <= current_slot)
|
||||
@@ -227,6 +284,51 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
info!("Historical block download complete");
|
||||
}
|
||||
|
||||
if is_custody_backfilling
|
||||
&& last_custody_backfill_log_slot
|
||||
.is_none_or(|slot| slot + BACKFILL_LOG_INTERVAL <= current_slot)
|
||||
{
|
||||
last_custody_backfill_log_slot = Some(current_slot);
|
||||
|
||||
let distance = format!(
|
||||
"{} slots ({})",
|
||||
sync_distance.as_u64(),
|
||||
slot_distance_pretty(sync_distance, slot_duration)
|
||||
);
|
||||
|
||||
let speed = speedo.slots_per_second();
|
||||
let display_speed = speed.is_some_and(|speed| speed != 0.0);
|
||||
let est_time_in_secs = if let (Some(da_boundary_epoch), Some(original_slot)) = (
|
||||
beacon_chain.get_column_da_boundary(),
|
||||
original_earliest_data_column_slot,
|
||||
) {
|
||||
let target = original_slot.saturating_sub(
|
||||
da_boundary_epoch.start_slot(T::EthSpec::slots_per_epoch()),
|
||||
);
|
||||
speedo.estimated_time_till_slot(target)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if display_speed {
|
||||
info!(
|
||||
distance,
|
||||
speed = sync_speed_pretty(speed),
|
||||
est_time = estimated_time_pretty(est_time_in_secs),
|
||||
"Downloading historical data columns"
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
distance,
|
||||
est_time = estimated_time_pretty(est_time_in_secs),
|
||||
"Downloading historical data columns"
|
||||
);
|
||||
}
|
||||
} else if !is_custody_backfilling && last_custody_backfill_log_slot.is_some() {
|
||||
last_custody_backfill_log_slot = None;
|
||||
original_earliest_data_column_slot = None;
|
||||
info!("Historical data column download complete");
|
||||
}
|
||||
|
||||
// Log if we are syncing
|
||||
if current_sync_state.is_syncing() {
|
||||
metrics::set_gauge(&metrics::IS_SYNCED, 0);
|
||||
@@ -267,8 +369,12 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
|
||||
let block_hash = match beacon_chain.canonical_head.head_execution_status() {
|
||||
Ok(ExecutionStatus::Irrelevant(_)) => "n/a".to_string(),
|
||||
Ok(ExecutionStatus::Valid(hash)) => format!("{} (verified)", hash),
|
||||
Ok(ExecutionStatus::Valid(hash)) => {
|
||||
metrics::set_gauge(&metrics::IS_OPTIMISTIC_SYNC, 0);
|
||||
format!("{} (verified)", hash)
|
||||
}
|
||||
Ok(ExecutionStatus::Optimistic(hash)) => {
|
||||
metrics::set_gauge(&metrics::IS_OPTIMISTIC_SYNC, 1);
|
||||
warn!(
|
||||
info = "chain not fully verified, \
|
||||
block and attestation production disabled until execution engine syncs",
|
||||
@@ -310,13 +416,8 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
);
|
||||
}
|
||||
|
||||
eth1_logging(&beacon_chain);
|
||||
bellatrix_readiness_logging(current_slot, &beacon_chain).await;
|
||||
capella_readiness_logging(current_slot, &beacon_chain).await;
|
||||
deneb_readiness_logging(current_slot, &beacon_chain).await;
|
||||
electra_readiness_logging(current_slot, &beacon_chain).await;
|
||||
eip7805_readiness_logging(current_slot, &beacon_chain).await;
|
||||
fulu_readiness_logging(current_slot, &beacon_chain).await;
|
||||
post_bellatrix_readiness_logging(current_slot, &beacon_chain).await;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -350,18 +451,6 @@ async fn bellatrix_readiness_logging<T: BeaconChainTypes>(
|
||||
return;
|
||||
}
|
||||
|
||||
if merge_completed && !has_execution_layer {
|
||||
// Logging of the EE being offline is handled in the other readiness logging functions.
|
||||
if !beacon_chain.is_time_to_prepare_for_capella(current_slot) {
|
||||
error!(
|
||||
info = "you need an execution engine to validate blocks, see: \
|
||||
https://lighthouse-book.sigmaprime.io/archived_merge_migration.html",
|
||||
"Execution endpoint required"
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
match beacon_chain.check_bellatrix_readiness(current_slot).await {
|
||||
BellatrixReadiness::Ready {
|
||||
config,
|
||||
@@ -410,265 +499,156 @@ async fn bellatrix_readiness_logging<T: BeaconChainTypes>(
|
||||
}
|
||||
|
||||
/// Provides some helpful logging to users to indicate if their node is ready for Capella
|
||||
async fn capella_readiness_logging<T: BeaconChainTypes>(
|
||||
async fn post_bellatrix_readiness_logging<T: BeaconChainTypes>(
|
||||
current_slot: Slot,
|
||||
beacon_chain: &BeaconChain<T>,
|
||||
) {
|
||||
let capella_completed = beacon_chain
|
||||
if let Some(fork) = find_next_fork_to_prepare(current_slot, beacon_chain) {
|
||||
let readiness = if let Some(el) = beacon_chain.execution_layer.as_ref() {
|
||||
match el
|
||||
.get_engine_capabilities(Some(Duration::from_secs(
|
||||
ENGINE_CAPABILITIES_REFRESH_INTERVAL,
|
||||
)))
|
||||
.await
|
||||
{
|
||||
Err(e) => Err(format!("Exchange capabilities failed: {e:?}")),
|
||||
Ok(capabilities) => {
|
||||
let missing_methods = methods_required_for_fork(fork, capabilities);
|
||||
if missing_methods.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Missing required methods: {missing_methods:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err("No execution endpoint".to_string())
|
||||
};
|
||||
|
||||
if let Err(readiness) = readiness {
|
||||
warn!(
|
||||
info = %readiness,
|
||||
"Not ready for {}", fork
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
info = "ensure the execution endpoint is updated to the latest release",
|
||||
"Ready for {}", fork
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_next_fork_to_prepare<T: BeaconChainTypes>(
|
||||
current_slot: Slot,
|
||||
beacon_chain: &BeaconChain<T>,
|
||||
) -> Option<ForkName> {
|
||||
let head_fork = beacon_chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.snapshot
|
||||
.beacon_state
|
||||
.fork_name_unchecked()
|
||||
.capella_enabled();
|
||||
.fork_name_unchecked();
|
||||
|
||||
let has_execution_layer = beacon_chain.execution_layer.is_some();
|
||||
|
||||
if capella_completed && has_execution_layer
|
||||
|| !beacon_chain.is_time_to_prepare_for_capella(current_slot)
|
||||
// Iterate forks from latest to oldest
|
||||
for (fork, fork_epoch) in ForkName::list_all_fork_epochs(&beacon_chain.spec)
|
||||
.iter()
|
||||
.rev()
|
||||
{
|
||||
return;
|
||||
// This readiness only handles capella and post fork
|
||||
if *fork <= ForkName::Bellatrix {
|
||||
break;
|
||||
}
|
||||
|
||||
// head state has already activated this fork
|
||||
if head_fork >= *fork {
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the first fork that is scheduled and close to happen
|
||||
if let Some(fork_epoch) = fork_epoch {
|
||||
let fork_slot = fork_epoch.start_slot(T::EthSpec::slots_per_epoch());
|
||||
let preparation_slots =
|
||||
FORK_READINESS_PREPARATION_SECONDS / beacon_chain.spec.seconds_per_slot;
|
||||
let in_fork_preparation_period = current_slot + preparation_slots > fork_slot;
|
||||
if in_fork_preparation_period {
|
||||
return Some(*fork);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if capella_completed && !has_execution_layer {
|
||||
// Logging of the EE being offline is handled in the other readiness logging functions.
|
||||
if !beacon_chain.is_time_to_prepare_for_deneb(current_slot) {
|
||||
error!(
|
||||
info = "you need a Capella enabled execution engine to validate blocks, see: \
|
||||
https://lighthouse-book.sigmaprime.io/archived_merge_migration.html",
|
||||
"Execution endpoint required"
|
||||
None
|
||||
}
|
||||
|
||||
fn methods_required_for_fork(
|
||||
fork: ForkName,
|
||||
capabilities: EngineCapabilities,
|
||||
) -> Vec<&'static str> {
|
||||
let mut missing_methods = vec![];
|
||||
match fork {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Bellatrix => {
|
||||
warn!(
|
||||
fork = %fork,
|
||||
"Invalid methods_required_for_fork call"
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
match beacon_chain.check_capella_readiness().await {
|
||||
CapellaReadiness::Ready => {
|
||||
info!(
|
||||
info = "ensure the execution endpoint is updated to the latest Capella/Shanghai release",
|
||||
"Ready for Capella"
|
||||
)
|
||||
ForkName::Capella => {
|
||||
if !capabilities.get_payload_v2 {
|
||||
missing_methods.push(ENGINE_GET_PAYLOAD_V2);
|
||||
}
|
||||
if !capabilities.forkchoice_updated_v2 {
|
||||
missing_methods.push(ENGINE_FORKCHOICE_UPDATED_V2);
|
||||
}
|
||||
if !capabilities.new_payload_v2 {
|
||||
missing_methods.push(ENGINE_NEW_PAYLOAD_V2);
|
||||
}
|
||||
}
|
||||
readiness @ CapellaReadiness::ExchangeCapabilitiesFailed { error: _ } => {
|
||||
error!(
|
||||
hint = "the execution endpoint may be offline",
|
||||
info = %readiness,
|
||||
"Not ready for Capella"
|
||||
)
|
||||
ForkName::Deneb => {
|
||||
if !capabilities.get_payload_v3 {
|
||||
missing_methods.push(ENGINE_GET_PAYLOAD_V3);
|
||||
}
|
||||
if !capabilities.forkchoice_updated_v3 {
|
||||
missing_methods.push(ENGINE_FORKCHOICE_UPDATED_V3);
|
||||
}
|
||||
if !capabilities.new_payload_v3 {
|
||||
missing_methods.push(ENGINE_NEW_PAYLOAD_V3);
|
||||
}
|
||||
}
|
||||
readiness => warn!(
|
||||
hint = "try updating the execution endpoint",
|
||||
info = %readiness,
|
||||
"Not ready for Capella"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides some helpful logging to users to indicate if their node is ready for Deneb
|
||||
async fn deneb_readiness_logging<T: BeaconChainTypes>(
|
||||
current_slot: Slot,
|
||||
beacon_chain: &BeaconChain<T>,
|
||||
) {
|
||||
let deneb_completed = beacon_chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.snapshot
|
||||
.beacon_state
|
||||
.fork_name_unchecked()
|
||||
.deneb_enabled();
|
||||
|
||||
let has_execution_layer = beacon_chain.execution_layer.is_some();
|
||||
|
||||
if deneb_completed && has_execution_layer
|
||||
|| !beacon_chain.is_time_to_prepare_for_deneb(current_slot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if deneb_completed && !has_execution_layer {
|
||||
error!(
|
||||
info = "you need a Deneb enabled execution engine to validate blocks.",
|
||||
"Execution endpoint required"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match beacon_chain.check_deneb_readiness().await {
|
||||
DenebReadiness::Ready => {
|
||||
info!(
|
||||
info =
|
||||
"ensure the execution endpoint is updated to the latest Deneb/Cancun release",
|
||||
"Ready for Deneb"
|
||||
)
|
||||
ForkName::Electra => {
|
||||
if !capabilities.get_payload_v4 {
|
||||
missing_methods.push(ENGINE_GET_PAYLOAD_V4);
|
||||
}
|
||||
if !capabilities.new_payload_v4 {
|
||||
missing_methods.push(ENGINE_NEW_PAYLOAD_V4);
|
||||
}
|
||||
}
|
||||
readiness @ DenebReadiness::ExchangeCapabilitiesFailed { error: _ } => {
|
||||
error!(
|
||||
hint = "the execution endpoint may be offline",
|
||||
info = %readiness,
|
||||
"Not ready for Deneb"
|
||||
)
|
||||
ForkName::Fulu => {
|
||||
if !capabilities.get_payload_v5 {
|
||||
missing_methods.push(ENGINE_GET_PAYLOAD_V5);
|
||||
}
|
||||
if !capabilities.new_payload_v4 {
|
||||
missing_methods.push(ENGINE_NEW_PAYLOAD_V4);
|
||||
}
|
||||
}
|
||||
readiness => warn!(
|
||||
hint = "try updating the execution endpoint",
|
||||
info = %readiness,
|
||||
"Not ready for Deneb"
|
||||
),
|
||||
}
|
||||
}
|
||||
/// Provides some helpful logging to users to indicate if their node is ready for Electra.
|
||||
async fn electra_readiness_logging<T: BeaconChainTypes>(
|
||||
current_slot: Slot,
|
||||
beacon_chain: &BeaconChain<T>,
|
||||
) {
|
||||
let electra_completed = beacon_chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.snapshot
|
||||
.beacon_state
|
||||
.fork_name_unchecked()
|
||||
.electra_enabled();
|
||||
|
||||
let has_execution_layer = beacon_chain.execution_layer.is_some();
|
||||
|
||||
if electra_completed && has_execution_layer
|
||||
|| !beacon_chain.is_time_to_prepare_for_electra(current_slot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if electra_completed && !has_execution_layer {
|
||||
// When adding a new fork, add a check for the next fork readiness here.
|
||||
error!(
|
||||
info = "you need a Electra enabled execution engine to validate blocks.",
|
||||
"Execution endpoint required"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match beacon_chain.check_electra_readiness().await {
|
||||
ElectraReadiness::Ready => {
|
||||
info!(
|
||||
info =
|
||||
"ensure the execution endpoint is updated to the latest Electra/Prague release",
|
||||
"Ready for Electra"
|
||||
)
|
||||
// TODO(EIP7805) check capabilities
|
||||
ForkName::Eip7805 => {
|
||||
if !capabilities.get_payload_v5 {
|
||||
missing_methods.push(ENGINE_GET_PAYLOAD_V5);
|
||||
}
|
||||
if !capabilities.new_payload_v4 {
|
||||
missing_methods.push(ENGINE_NEW_PAYLOAD_V4);
|
||||
}
|
||||
}
|
||||
readiness @ ElectraReadiness::ExchangeCapabilitiesFailed { error: _ } => {
|
||||
error!(
|
||||
hint = "the execution endpoint may be offline",
|
||||
info = %readiness,
|
||||
"Not ready for Electra"
|
||||
)
|
||||
ForkName::Gloas => {
|
||||
if !capabilities.get_payload_v5 {
|
||||
missing_methods.push(ENGINE_GET_PAYLOAD_V5);
|
||||
}
|
||||
if !capabilities.new_payload_v4 {
|
||||
missing_methods.push(ENGINE_NEW_PAYLOAD_V4);
|
||||
}
|
||||
}
|
||||
readiness => warn!(
|
||||
hint = "try updating the execution endpoint",
|
||||
info = %readiness,
|
||||
"Not ready for Electra"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides some helpful logging to users to indicate if their node is ready for Eip7805.
|
||||
async fn eip7805_readiness_logging<T: BeaconChainTypes>(
|
||||
current_slot: Slot,
|
||||
beacon_chain: &BeaconChain<T>,
|
||||
) {
|
||||
let eip7805_completed = beacon_chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.snapshot
|
||||
.beacon_state
|
||||
.fork_name_unchecked()
|
||||
.eip7805_enabled();
|
||||
|
||||
let has_execution_layer = beacon_chain.execution_layer.is_some();
|
||||
|
||||
if eip7805_completed && has_execution_layer
|
||||
|| !beacon_chain.is_time_to_prepare_for_electra(current_slot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if eip7805_completed && !has_execution_layer {
|
||||
// When adding a new fork, add a check for the next fork readiness here.
|
||||
error!(
|
||||
info = "you need a Eip7805 enabled execution engine to validate blocks.",
|
||||
"Execution endpoint required"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match beacon_chain.check_eip7805_readiness().await {
|
||||
Eip7805Readiness::Ready => {
|
||||
info!(
|
||||
info = "ensure the execution endpoint is updated to the latest eip7805 release",
|
||||
"Ready for Eip7805"
|
||||
)
|
||||
}
|
||||
readiness @ Eip7805Readiness::ExchangeCapabilitiesFailed { error: _ } => {
|
||||
error!(
|
||||
hint = "the execution endpoint may be offline",
|
||||
info = %readiness,
|
||||
"Not ready for Eip7805Readiness"
|
||||
)
|
||||
}
|
||||
readiness => warn!(
|
||||
hint = "try updating the execution endpoint",
|
||||
info = %readiness,
|
||||
"Not ready for Eip7805Readiness"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides some helpful logging to users to indicate if their node is ready for Fulu.
|
||||
async fn fulu_readiness_logging<T: BeaconChainTypes>(
|
||||
current_slot: Slot,
|
||||
beacon_chain: &BeaconChain<T>,
|
||||
) {
|
||||
let fulu_completed = beacon_chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.snapshot
|
||||
.beacon_state
|
||||
.fork_name_unchecked()
|
||||
.fulu_enabled();
|
||||
|
||||
let has_execution_layer = beacon_chain.execution_layer.is_some();
|
||||
|
||||
if fulu_completed && has_execution_layer
|
||||
|| !beacon_chain.is_time_to_prepare_for_fulu(current_slot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if fulu_completed && !has_execution_layer {
|
||||
error!(
|
||||
info = "you need a Fulu enabled execution engine to validate blocks.",
|
||||
"Execution endpoint required"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match beacon_chain.check_fulu_readiness().await {
|
||||
FuluReadiness::Ready => {
|
||||
info!(
|
||||
info = "ensure the execution endpoint is updated to the latest Fulu release",
|
||||
"Ready for Fulu"
|
||||
)
|
||||
}
|
||||
readiness @ FuluReadiness::ExchangeCapabilitiesFailed { error: _ } => {
|
||||
error!(
|
||||
hint = "the execution endpoint may be offline",
|
||||
info = %readiness,
|
||||
"Not ready for Fulu"
|
||||
)
|
||||
}
|
||||
readiness => warn!(
|
||||
hint = "try updating the execution endpoint",
|
||||
info = %readiness,
|
||||
"Not ready for Fulu"
|
||||
),
|
||||
}
|
||||
missing_methods
|
||||
}
|
||||
|
||||
async fn genesis_execution_payload_logging<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>) {
|
||||
@@ -731,53 +711,6 @@ async fn genesis_execution_payload_logging<T: BeaconChainTypes>(beacon_chain: &B
|
||||
}
|
||||
}
|
||||
|
||||
fn eth1_logging<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>) {
|
||||
let current_slot_opt = beacon_chain.slot().ok();
|
||||
|
||||
// Perform some logging about the eth1 chain
|
||||
if let Some(eth1_chain) = beacon_chain.eth1_chain.as_ref() {
|
||||
// No need to do logging if using the dummy backend.
|
||||
if eth1_chain.is_dummy_backend() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(status) = eth1_chain.sync_status(
|
||||
beacon_chain.genesis_time,
|
||||
current_slot_opt,
|
||||
&beacon_chain.spec,
|
||||
) {
|
||||
debug!(
|
||||
eth1_head_block = status.head_block_number,
|
||||
latest_cached_block_number = status.latest_cached_block_number,
|
||||
latest_cached_timestamp = status.latest_cached_block_timestamp,
|
||||
voting_target_timestamp = status.voting_target_timestamp,
|
||||
ready = status.lighthouse_is_cached_and_ready,
|
||||
"Eth1 cache sync status"
|
||||
);
|
||||
|
||||
if !status.lighthouse_is_cached_and_ready {
|
||||
let voting_target_timestamp = status.voting_target_timestamp;
|
||||
|
||||
let distance = status
|
||||
.latest_cached_block_timestamp
|
||||
.map(|latest| {
|
||||
voting_target_timestamp.saturating_sub(latest)
|
||||
/ beacon_chain.spec.seconds_per_eth1_block
|
||||
})
|
||||
.map(|distance| distance.to_string())
|
||||
.unwrap_or_else(|| "initializing deposits".to_string());
|
||||
|
||||
warn!(
|
||||
est_blocks_remaining = distance,
|
||||
"Syncing deposit contract block cache"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
error!("Unable to determine deposit contract sync status");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the peer count, returning something helpful if it's `usize::MAX` (effectively a
|
||||
/// `None` value).
|
||||
fn peer_count_pretty(peer_count: usize) -> String {
|
||||
|
||||
Reference in New Issue
Block a user