Log ttd (#3339)
## Issue Addressed Resolves #3249 ## Proposed Changes Log merge related parameters and EE status in the beacon notifier before the merge. Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
parent
7c3ff903ca
commit
5b5cf9cfaa
@ -20,6 +20,7 @@ pub mod fork_choice_signal;
|
||||
pub mod fork_revert;
|
||||
mod head_tracker;
|
||||
pub mod historical_blocks;
|
||||
pub mod merge_readiness;
|
||||
mod metrics;
|
||||
pub mod migrate;
|
||||
mod naive_aggregation_pool;
|
||||
|
169
beacon_node/beacon_chain/src/merge_readiness.rs
Normal file
169
beacon_node/beacon_chain/src/merge_readiness.rs
Normal file
@ -0,0 +1,169 @@
|
||||
//! Provides tools for checking if a node is ready for the Bellatrix upgrade and following merge
|
||||
//! transition.
|
||||
|
||||
use crate::{BeaconChain, BeaconChainTypes};
|
||||
use execution_layer::Error as EngineError;
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
use types::*;
|
||||
|
||||
/// The time before the Bellatrix fork when we will start issuing warnings about preparation.
|
||||
const SECONDS_IN_A_WEEK: u64 = 604800;
|
||||
pub const MERGE_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct MergeConfig {
|
||||
pub terminal_total_difficulty: Option<Uint256>,
|
||||
pub terminal_block_hash: Option<ExecutionBlockHash>,
|
||||
pub terminal_block_hash_epoch: Option<Epoch>,
|
||||
}
|
||||
|
||||
impl fmt::Display for MergeConfig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.terminal_block_hash.is_none()
|
||||
&& self.terminal_block_hash_epoch.is_none()
|
||||
&& self.terminal_total_difficulty.is_none()
|
||||
{
|
||||
return write!(
|
||||
f,
|
||||
"Merge terminal difficulty parameters not configured, check your config"
|
||||
);
|
||||
}
|
||||
let mut display_string = String::new();
|
||||
if let Some(terminal_total_difficulty) = self.terminal_total_difficulty {
|
||||
write!(
|
||||
display_string,
|
||||
"terminal_total_difficulty: {},",
|
||||
terminal_total_difficulty
|
||||
)?;
|
||||
}
|
||||
if let Some(terminal_block_hash) = self.terminal_block_hash {
|
||||
write!(
|
||||
display_string,
|
||||
"terminal_block_hash: {},",
|
||||
terminal_block_hash
|
||||
)?;
|
||||
}
|
||||
if let Some(terminal_block_hash_epoch) = self.terminal_block_hash_epoch {
|
||||
write!(
|
||||
display_string,
|
||||
"terminal_block_hash_epoch: {},",
|
||||
terminal_block_hash_epoch
|
||||
)?;
|
||||
}
|
||||
write!(f, "{}", display_string.trim_end_matches(','))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl MergeConfig {
|
||||
/// Instantiate `self` from the values in a `ChainSpec`.
|
||||
pub fn from_chainspec(spec: &ChainSpec) -> Self {
|
||||
let mut params = MergeConfig::default();
|
||||
if spec.terminal_total_difficulty != Uint256::max_value() {
|
||||
params.terminal_total_difficulty = Some(spec.terminal_total_difficulty);
|
||||
}
|
||||
if spec.terminal_block_hash != ExecutionBlockHash::zero() {
|
||||
params.terminal_block_hash = Some(spec.terminal_block_hash);
|
||||
}
|
||||
if spec.terminal_block_hash_activation_epoch != Epoch::max_value() {
|
||||
params.terminal_block_hash_epoch = Some(spec.terminal_block_hash_activation_epoch);
|
||||
}
|
||||
params
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates if a node is ready for the Bellatrix upgrade and subsequent merge transition.
|
||||
pub enum MergeReadiness {
|
||||
/// The node is ready, as far as we can tell.
|
||||
Ready {
|
||||
config: MergeConfig,
|
||||
current_difficulty: Result<Uint256, String>,
|
||||
},
|
||||
/// The transition configuration with the EL failed, there might be a problem with
|
||||
/// connectivity, authentication or a difference in configuration.
|
||||
ExchangeTransitionConfigurationFailed(EngineError),
|
||||
/// The EL can be reached and has the correct configuration, however it's not yet synced.
|
||||
NotSynced,
|
||||
/// The user has not configured this node to use an execution endpoint.
|
||||
NoExecutionEndpoint,
|
||||
}
|
||||
|
||||
impl fmt::Display for MergeReadiness {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
MergeReadiness::Ready {
|
||||
config: params,
|
||||
current_difficulty,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"This node appears ready for the merge. \
|
||||
Params: {}, current_difficulty: {:?}",
|
||||
params, current_difficulty
|
||||
)
|
||||
}
|
||||
MergeReadiness::ExchangeTransitionConfigurationFailed(e) => write!(
|
||||
f,
|
||||
"Could not confirm the transition configuration with the \
|
||||
execution endpoint: {:?}",
|
||||
e
|
||||
),
|
||||
MergeReadiness::NotSynced => write!(
|
||||
f,
|
||||
"The execution endpoint is connected and configured, \
|
||||
however it is not yet synced"
|
||||
),
|
||||
MergeReadiness::NoExecutionEndpoint => write!(
|
||||
f,
|
||||
"The --execution-endpoint flag is not specified, this is a \
|
||||
requirement for the merge"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Returns `true` if the Bellatrix fork has occurred or will occur within
|
||||
/// `MERGE_READINESS_PREPARATION_SECONDS`.
|
||||
pub fn is_time_to_prepare_for_bellatrix(&self, current_slot: Slot) -> bool {
|
||||
if let Some(bellatrix_epoch) = self.spec.bellatrix_fork_epoch {
|
||||
let bellatrix_slot = bellatrix_epoch.start_slot(T::EthSpec::slots_per_epoch());
|
||||
let merge_readiness_preparation_slots =
|
||||
MERGE_READINESS_PREPARATION_SECONDS / self.spec.seconds_per_slot;
|
||||
|
||||
// Return `true` if Bellatrix has happened or is within the preparation time.
|
||||
current_slot + merge_readiness_preparation_slots > bellatrix_slot
|
||||
} else {
|
||||
// The Bellatrix fork epoch has not been defined yet, no need to prepare.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to connect to the EL and confirm that it is ready for the merge.
|
||||
pub async fn check_merge_readiness(&self) -> MergeReadiness {
|
||||
if let Some(el) = self.execution_layer.as_ref() {
|
||||
if let Err(e) = el.exchange_transition_configuration(&self.spec).await {
|
||||
// The EL was either unreachable, responded with an error or has a different
|
||||
// configuration.
|
||||
return MergeReadiness::ExchangeTransitionConfigurationFailed(e);
|
||||
}
|
||||
|
||||
if !el.is_synced_for_notifier().await {
|
||||
// The EL is not synced.
|
||||
return MergeReadiness::NotSynced;
|
||||
}
|
||||
let params = MergeConfig::from_chainspec(&self.spec);
|
||||
let current_difficulty = el
|
||||
.get_current_difficulty()
|
||||
.await
|
||||
.map_err(|_| "Failed to get current difficulty from execution node".to_string());
|
||||
MergeReadiness::Ready {
|
||||
config: params,
|
||||
current_difficulty,
|
||||
}
|
||||
} else {
|
||||
// There is no EL configured.
|
||||
MergeReadiness::NoExecutionEndpoint
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
use crate::metrics;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, ExecutionStatus};
|
||||
use beacon_chain::{
|
||||
merge_readiness::{MergeConfig, MergeReadiness},
|
||||
BeaconChain, BeaconChainTypes, ExecutionStatus,
|
||||
};
|
||||
use lighthouse_network::{types::SyncState, NetworkGlobals};
|
||||
use parking_lot::Mutex;
|
||||
use slog::{crit, debug, error, info, warn, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::time::sleep;
|
||||
use types::{EthSpec, Slot};
|
||||
use types::*;
|
||||
|
||||
/// Create a warning log whenever the peer count is at or below this value.
|
||||
pub const WARN_PEER_COUNT: usize = 1;
|
||||
@ -77,6 +80,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
|
||||
// Perform post-genesis logging.
|
||||
let mut last_backfill_log_slot = None;
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
let connected_peer_count = network.connected_peers();
|
||||
@ -87,12 +91,12 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
match (current_sync_state, &sync_state) {
|
||||
(_, SyncState::BackFillSyncing { .. }) => {
|
||||
// We have transitioned to a backfill sync. Reset the speedo.
|
||||
let mut speedo = speedo.lock();
|
||||
let mut speedo = speedo.lock().await;
|
||||
speedo.clear();
|
||||
}
|
||||
(SyncState::BackFillSyncing { .. }, _) => {
|
||||
// We have transitioned from a backfill sync, reset the speedo
|
||||
let mut speedo = speedo.lock();
|
||||
let mut speedo = speedo.lock().await;
|
||||
speedo.clear();
|
||||
}
|
||||
(_, _) => {}
|
||||
@ -125,7 +129,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
// progress.
|
||||
let mut sync_distance = current_slot - head_slot;
|
||||
|
||||
let mut speedo = speedo.lock();
|
||||
let mut speedo = speedo.lock().await;
|
||||
match current_sync_state {
|
||||
SyncState::BackFillSyncing { .. } => {
|
||||
// Observe backfilling sync info.
|
||||
@ -306,6 +310,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
}
|
||||
|
||||
eth1_logging(&beacon_chain, &log);
|
||||
merge_readiness_logging(current_slot, &beacon_chain, &log).await;
|
||||
}
|
||||
};
|
||||
|
||||
@ -315,6 +320,88 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Provides some helpful logging to users to indicate if their node is ready for the Bellatrix
|
||||
/// fork and subsequent merge transition.
|
||||
async fn merge_readiness_logging<T: BeaconChainTypes>(
|
||||
current_slot: Slot,
|
||||
beacon_chain: &BeaconChain<T>,
|
||||
log: &Logger,
|
||||
) {
|
||||
let merge_completed = beacon_chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.snapshot
|
||||
.beacon_block
|
||||
.message()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.map_or(false, |payload| {
|
||||
payload.parent_hash() != ExecutionBlockHash::zero()
|
||||
});
|
||||
|
||||
if merge_completed || !beacon_chain.is_time_to_prepare_for_bellatrix(current_slot) {
|
||||
return;
|
||||
}
|
||||
|
||||
match beacon_chain.check_merge_readiness().await {
|
||||
MergeReadiness::Ready {
|
||||
config,
|
||||
current_difficulty,
|
||||
} => match config {
|
||||
MergeConfig {
|
||||
terminal_total_difficulty: Some(ttd),
|
||||
terminal_block_hash: None,
|
||||
terminal_block_hash_epoch: None,
|
||||
} => {
|
||||
info!(
|
||||
log,
|
||||
"Ready for the merge";
|
||||
"terminal_total_difficulty" => %ttd,
|
||||
"current_difficulty" => current_difficulty
|
||||
.map(|d| d.to_string())
|
||||
.unwrap_or_else(|_| "??".into()),
|
||||
)
|
||||
}
|
||||
MergeConfig {
|
||||
terminal_total_difficulty: _,
|
||||
terminal_block_hash: Some(terminal_block_hash),
|
||||
terminal_block_hash_epoch: Some(terminal_block_hash_epoch),
|
||||
} => {
|
||||
info!(
|
||||
log,
|
||||
"Ready for the merge";
|
||||
"info" => "you are using override parameters, please ensure that you \
|
||||
understand these parameters and their implications.",
|
||||
"terminal_block_hash" => ?terminal_block_hash,
|
||||
"terminal_block_hash_epoch" => ?terminal_block_hash_epoch,
|
||||
)
|
||||
}
|
||||
other => error!(
|
||||
log,
|
||||
"Inconsistent merge configuration";
|
||||
"config" => ?other
|
||||
),
|
||||
},
|
||||
readiness @ MergeReadiness::ExchangeTransitionConfigurationFailed(_) => {
|
||||
error!(
|
||||
log,
|
||||
"Not ready for merge";
|
||||
"info" => %readiness,
|
||||
)
|
||||
}
|
||||
readiness @ MergeReadiness::NotSynced => warn!(
|
||||
log,
|
||||
"Not ready for merge";
|
||||
"info" => %readiness,
|
||||
),
|
||||
readiness @ MergeReadiness::NoExecutionEndpoint => warn!(
|
||||
log,
|
||||
"Not ready for merge";
|
||||
"info" => %readiness,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn eth1_logging<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>, log: &Logger) {
|
||||
let current_slot_opt = beacon_chain.slot().ok();
|
||||
|
||||
|
@ -234,6 +234,16 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&self.inner.executor
|
||||
}
|
||||
|
||||
/// Get the current difficulty of the PoW chain.
|
||||
pub async fn get_current_difficulty(&self) -> Result<Uint256, ApiError> {
|
||||
let block = self
|
||||
.engine()
|
||||
.api
|
||||
.get_block_by_number(BlockByNumberQuery::Tag(LATEST_TAG))
|
||||
.await?
|
||||
.ok_or(ApiError::ExecutionHeadBlockNotFound)?;
|
||||
Ok(block.total_difficulty)
|
||||
}
|
||||
/// Note: this function returns a mutex guard, be careful to avoid deadlocks.
|
||||
async fn execution_blocks(
|
||||
&self,
|
||||
@ -355,6 +365,29 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
self.engine().is_synced().await
|
||||
}
|
||||
|
||||
/// Execution nodes return a "SYNCED" response when they do not have any peers.
|
||||
///
|
||||
/// This function is a wrapper over `Self::is_synced` that makes an additional
|
||||
/// check for the execution layer sync status. Checks if the latest block has
|
||||
/// a `block_number != 0`.
|
||||
/// Returns the `Self::is_synced` response if unable to get latest block.
|
||||
pub async fn is_synced_for_notifier(&self) -> bool {
|
||||
let synced = self.is_synced().await;
|
||||
if synced {
|
||||
if let Ok(Some(block)) = self
|
||||
.engine()
|
||||
.api
|
||||
.get_block_by_number(BlockByNumberQuery::Tag(LATEST_TAG))
|
||||
.await
|
||||
{
|
||||
if block.block_number == 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
synced
|
||||
}
|
||||
|
||||
/// Updates the proposer preparation data provided by validators
|
||||
pub async fn update_proposer_preparation(
|
||||
&self,
|
||||
|
@ -132,6 +132,15 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
|
||||
Ok(serde_json::to_value(response).unwrap())
|
||||
}
|
||||
ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1 => {
|
||||
let block_generator = ctx.execution_block_generator.read();
|
||||
let transition_config: TransitionConfigurationV1 = TransitionConfigurationV1 {
|
||||
terminal_total_difficulty: block_generator.terminal_total_difficulty,
|
||||
terminal_block_hash: block_generator.terminal_block_hash,
|
||||
terminal_block_number: block_generator.terminal_block_number,
|
||||
};
|
||||
Ok(serde_json::to_value(transition_config).unwrap())
|
||||
}
|
||||
other => Err(format!(
|
||||
"The method {} does not exist/is not available",
|
||||
other
|
||||
|
Loading…
Reference in New Issue
Block a user