d53d43844c
* Remove CapellaReadiness::NotSynced Some EEs have a habit of flipping between synced/not-synced, which causes some spurious "Not read for the merge" messages back before the merge. For the merge, if the EE wasn't synced the CE simple wouldn't go through the transition (due to optimistic sync stuff). However, we don't have that hard requirement for Capella; the CE will go through the fork and just wait for the EE to catch up. I think that removing `NotSynced` here will avoid false-positives on the "Not ready logs..". We'll be creating other WARN/ERRO logs if the EE isn't synced, anyway. * Change some Capella readiness logging There's two changes here: 1. Shorten the log messages, for readability. 2. Change the hints. Connecting a Capella-ready LH to a non-Capella-ready EE gives this log: ``` WARN Not ready for Capella info: The execution endpoint does not appear to support the required engine api methods for Capella: Required Methods Unsupported: engine_getPayloadV2 engine_forkchoiceUpdatedV2 engine_newPayloadV2, service: slot_notifier ``` This variant of error doesn't get a "try updating" style hint, when it's the one that needs it. This is because we detect the method-not-found reponse from the EE and return default capabilities, rather than indicating that the request fails. I think it's fair to say that an EE upgrade is required whenever it doesn't provide the required methods. I changed the `ExchangeCapabilitiesFailed` message since that can only happen when the EE fails to respond with anything other than success or not-found.
123 lines
4.9 KiB
Rust
123 lines
4.9 KiB
Rust
//! Provides tools for checking if a node is ready for the Capella upgrade and following merge
|
|
//! transition.
|
|
|
|
use crate::{BeaconChain, BeaconChainTypes};
|
|
use execution_layer::http::{
|
|
ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_GET_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V2,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::fmt;
|
|
use std::time::Duration;
|
|
use types::*;
|
|
|
|
/// The time before the Capella fork when we will start issuing warnings about preparation.
|
|
use super::merge_readiness::SECONDS_IN_A_WEEK;
|
|
pub const CAPELLA_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK * 2;
|
|
pub const ENGINE_CAPABILITIES_REFRESH_INTERVAL: u64 = 300;
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[serde(tag = "type")]
|
|
pub enum CapellaReadiness {
|
|
/// The execution engine is capella-enabled (as far as we can tell)
|
|
Ready,
|
|
/// We are connected to an execution engine which doesn't support the V2 engine api methods
|
|
V2MethodsNotSupported { error: String },
|
|
/// The transition configuration with the EL failed, there might be a problem with
|
|
/// connectivity, authentication or a difference in configuration.
|
|
ExchangeCapabilitiesFailed { error: String },
|
|
/// The user has not configured an execution endpoint
|
|
NoExecutionEndpoint,
|
|
}
|
|
|
|
impl fmt::Display for CapellaReadiness {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
CapellaReadiness::Ready => {
|
|
write!(f, "This node appears ready for Capella.")
|
|
}
|
|
CapellaReadiness::ExchangeCapabilitiesFailed { error } => write!(
|
|
f,
|
|
"Could not exchange capabilities with the \
|
|
execution endpoint: {}",
|
|
error
|
|
),
|
|
CapellaReadiness::NoExecutionEndpoint => write!(
|
|
f,
|
|
"The --execution-endpoint flag is not specified, this is a \
|
|
requirement post-merge"
|
|
),
|
|
CapellaReadiness::V2MethodsNotSupported { error } => write!(
|
|
f,
|
|
"Execution endpoint does not support Capella methods: {}",
|
|
error
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: BeaconChainTypes> BeaconChain<T> {
|
|
/// Returns `true` if capella epoch is set and Capella fork has occurred or will
|
|
/// occur within `CAPELLA_READINESS_PREPARATION_SECONDS`
|
|
pub fn is_time_to_prepare_for_capella(&self, current_slot: Slot) -> bool {
|
|
if let Some(capella_epoch) = self.spec.capella_fork_epoch {
|
|
let capella_slot = capella_epoch.start_slot(T::EthSpec::slots_per_epoch());
|
|
let capella_readiness_preparation_slots =
|
|
CAPELLA_READINESS_PREPARATION_SECONDS / self.spec.seconds_per_slot;
|
|
// Return `true` if Capella has happened or is within the preparation time.
|
|
current_slot + capella_readiness_preparation_slots > capella_slot
|
|
} else {
|
|
// The Capella 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 capella.
|
|
pub async fn check_capella_readiness(&self) -> CapellaReadiness {
|
|
if let Some(el) = self.execution_layer.as_ref() {
|
|
match el
|
|
.get_engine_capabilities(Some(Duration::from_secs(
|
|
ENGINE_CAPABILITIES_REFRESH_INTERVAL,
|
|
)))
|
|
.await
|
|
{
|
|
Err(e) => {
|
|
// The EL was either unreachable or responded with an error
|
|
CapellaReadiness::ExchangeCapabilitiesFailed {
|
|
error: format!("{:?}", e),
|
|
}
|
|
}
|
|
Ok(capabilities) => {
|
|
let mut missing_methods = String::from("Required Methods Unsupported:");
|
|
let mut all_good = true;
|
|
if !capabilities.get_payload_v2 {
|
|
missing_methods.push(' ');
|
|
missing_methods.push_str(ENGINE_GET_PAYLOAD_V2);
|
|
all_good = false;
|
|
}
|
|
if !capabilities.forkchoice_updated_v2 {
|
|
missing_methods.push(' ');
|
|
missing_methods.push_str(ENGINE_FORKCHOICE_UPDATED_V2);
|
|
all_good = false;
|
|
}
|
|
if !capabilities.new_payload_v2 {
|
|
missing_methods.push(' ');
|
|
missing_methods.push_str(ENGINE_NEW_PAYLOAD_V2);
|
|
all_good = false;
|
|
}
|
|
|
|
if all_good {
|
|
CapellaReadiness::Ready
|
|
} else {
|
|
CapellaReadiness::V2MethodsNotSupported {
|
|
error: missing_methods,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
CapellaReadiness::NoExecutionEndpoint
|
|
}
|
|
}
|
|
}
|