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;
|
pub mod fork_revert;
|
||||||
mod head_tracker;
|
mod head_tracker;
|
||||||
pub mod historical_blocks;
|
pub mod historical_blocks;
|
||||||
|
pub mod merge_readiness;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
pub mod migrate;
|
pub mod migrate;
|
||||||
mod naive_aggregation_pool;
|
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 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 lighthouse_network::{types::SyncState, NetworkGlobals};
|
||||||
use parking_lot::Mutex;
|
|
||||||
use slog::{crit, debug, error, info, warn, Logger};
|
use slog::{crit, debug, error, info, warn, Logger};
|
||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use types::{EthSpec, Slot};
|
use types::*;
|
||||||
|
|
||||||
/// Create a warning log whenever the peer count is at or below this value.
|
/// Create a warning log whenever the peer count is at or below this value.
|
||||||
pub const WARN_PEER_COUNT: usize = 1;
|
pub const WARN_PEER_COUNT: usize = 1;
|
||||||
@ -77,6 +80,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
|||||||
|
|
||||||
// Perform post-genesis logging.
|
// Perform post-genesis logging.
|
||||||
let mut last_backfill_log_slot = None;
|
let mut last_backfill_log_slot = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
let connected_peer_count = network.connected_peers();
|
let connected_peer_count = network.connected_peers();
|
||||||
@ -87,12 +91,12 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
|||||||
match (current_sync_state, &sync_state) {
|
match (current_sync_state, &sync_state) {
|
||||||
(_, SyncState::BackFillSyncing { .. }) => {
|
(_, SyncState::BackFillSyncing { .. }) => {
|
||||||
// We have transitioned to a backfill sync. Reset the speedo.
|
// We have transitioned to a backfill sync. Reset the speedo.
|
||||||
let mut speedo = speedo.lock();
|
let mut speedo = speedo.lock().await;
|
||||||
speedo.clear();
|
speedo.clear();
|
||||||
}
|
}
|
||||||
(SyncState::BackFillSyncing { .. }, _) => {
|
(SyncState::BackFillSyncing { .. }, _) => {
|
||||||
// We have transitioned from a backfill sync, reset the speedo
|
// We have transitioned from a backfill sync, reset the speedo
|
||||||
let mut speedo = speedo.lock();
|
let mut speedo = speedo.lock().await;
|
||||||
speedo.clear();
|
speedo.clear();
|
||||||
}
|
}
|
||||||
(_, _) => {}
|
(_, _) => {}
|
||||||
@ -125,7 +129,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
|||||||
// progress.
|
// progress.
|
||||||
let mut sync_distance = current_slot - head_slot;
|
let mut sync_distance = current_slot - head_slot;
|
||||||
|
|
||||||
let mut speedo = speedo.lock();
|
let mut speedo = speedo.lock().await;
|
||||||
match current_sync_state {
|
match current_sync_state {
|
||||||
SyncState::BackFillSyncing { .. } => {
|
SyncState::BackFillSyncing { .. } => {
|
||||||
// Observe backfilling sync info.
|
// Observe backfilling sync info.
|
||||||
@ -306,6 +310,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
eth1_logging(&beacon_chain, &log);
|
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(())
|
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) {
|
fn eth1_logging<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>, log: &Logger) {
|
||||||
let current_slot_opt = beacon_chain.slot().ok();
|
let current_slot_opt = beacon_chain.slot().ok();
|
||||||
|
|
||||||
|
@ -234,6 +234,16 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
&self.inner.executor
|
&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.
|
/// Note: this function returns a mutex guard, be careful to avoid deadlocks.
|
||||||
async fn execution_blocks(
|
async fn execution_blocks(
|
||||||
&self,
|
&self,
|
||||||
@ -355,6 +365,29 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
self.engine().is_synced().await
|
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
|
/// Updates the proposer preparation data provided by validators
|
||||||
pub async fn update_proposer_preparation(
|
pub async fn update_proposer_preparation(
|
||||||
&self,
|
&self,
|
||||||
|
@ -132,6 +132,15 @@ pub async fn handle_rpc<T: EthSpec>(
|
|||||||
|
|
||||||
Ok(serde_json::to_value(response).unwrap())
|
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!(
|
other => Err(format!(
|
||||||
"The method {} does not exist/is not available",
|
"The method {} does not exist/is not available",
|
||||||
other
|
other
|
||||||
|
Loading…
Reference in New Issue
Block a user