mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 13:24:44 +00:00
Add Fulu boilerplate (#6695)
* Add Fulu boilerplate * Add more boilerplate * Change fulu_time to osaka_time * Merge branch 'unstable' into fulu-boilerplate * Fix tests * Merge branch 'unstable' into fulu-boilerplate * More test fixes * Apply suggestions * Remove `get_payload` boilerplate * Add lightclient fulu types and fix beacon-chain-tests * Disable Fulu in ef-tests * Reduce boilerplate for future forks * Small fixes * One more fix * Apply suggestions * Merge branch 'unstable' into fulu-boilerplate * Fix lints
This commit is contained in:
@@ -51,13 +51,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.get_state(&state_root, Some(state_slot))?
|
||||
.ok_or(BeaconChainError::MissingBeaconState(state_root))?;
|
||||
|
||||
match state {
|
||||
BeaconState::Base(_) => self.compute_attestation_rewards_base(state, validators),
|
||||
BeaconState::Altair(_)
|
||||
| BeaconState::Bellatrix(_)
|
||||
| BeaconState::Capella(_)
|
||||
| BeaconState::Deneb(_)
|
||||
| BeaconState::Electra(_) => self.compute_attestation_rewards_altair(state, validators),
|
||||
if state.fork_name_unchecked().altair_enabled() {
|
||||
self.compute_attestation_rewards_altair(state, validators)
|
||||
} else {
|
||||
self.compute_attestation_rewards_base(state, validators)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use types::{
|
||||
};
|
||||
use types::{
|
||||
ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadElectra,
|
||||
ExecutionPayloadHeader,
|
||||
ExecutionPayloadFulu, ExecutionPayloadHeader,
|
||||
};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
@@ -99,6 +99,7 @@ fn reconstruct_default_header_block<E: EthSpec>(
|
||||
ForkName::Capella => ExecutionPayloadCapella::default().into(),
|
||||
ForkName::Deneb => ExecutionPayloadDeneb::default().into(),
|
||||
ForkName::Electra => ExecutionPayloadElectra::default().into(),
|
||||
ForkName::Fulu => ExecutionPayloadFulu::default().into(),
|
||||
ForkName::Base | ForkName::Altair => {
|
||||
return Err(Error::PayloadReconstruction(format!(
|
||||
"Block with fork variant {} has execution payload",
|
||||
@@ -742,13 +743,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn check_all_blocks_from_altair_to_electra() {
|
||||
async fn check_all_blocks_from_altair_to_fulu() {
|
||||
let slots_per_epoch = MinimalEthSpec::slots_per_epoch() as usize;
|
||||
let num_epochs = 10;
|
||||
let num_epochs = 12;
|
||||
let bellatrix_fork_epoch = 2usize;
|
||||
let capella_fork_epoch = 4usize;
|
||||
let deneb_fork_epoch = 6usize;
|
||||
let electra_fork_epoch = 8usize;
|
||||
let fulu_fork_epoch = 10usize;
|
||||
let num_blocks_produced = num_epochs * slots_per_epoch;
|
||||
|
||||
let mut spec = test_spec::<MinimalEthSpec>();
|
||||
@@ -757,6 +759,7 @@ mod tests {
|
||||
spec.capella_fork_epoch = Some(Epoch::new(capella_fork_epoch as u64));
|
||||
spec.deneb_fork_epoch = Some(Epoch::new(deneb_fork_epoch as u64));
|
||||
spec.electra_fork_epoch = Some(Epoch::new(electra_fork_epoch as u64));
|
||||
spec.fulu_fork_epoch = Some(Epoch::new(fulu_fork_epoch as u64));
|
||||
let spec = Arc::new(spec);
|
||||
|
||||
let harness = get_harness(VALIDATOR_COUNT, spec.clone());
|
||||
|
||||
@@ -5317,23 +5317,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
// If required, start the process of loading an execution payload from the EL early. This
|
||||
// allows it to run concurrently with things like attestation packing.
|
||||
let prepare_payload_handle = match &state {
|
||||
BeaconState::Base(_) | BeaconState::Altair(_) => None,
|
||||
BeaconState::Bellatrix(_)
|
||||
| BeaconState::Capella(_)
|
||||
| BeaconState::Deneb(_)
|
||||
| BeaconState::Electra(_) => {
|
||||
let prepare_payload_handle = get_execution_payload(
|
||||
self.clone(),
|
||||
&state,
|
||||
parent_root,
|
||||
proposer_index,
|
||||
builder_params,
|
||||
builder_boost_factor,
|
||||
block_production_version,
|
||||
)?;
|
||||
Some(prepare_payload_handle)
|
||||
}
|
||||
let prepare_payload_handle = if state.fork_name_unchecked().bellatrix_enabled() {
|
||||
let prepare_payload_handle = get_execution_payload(
|
||||
self.clone(),
|
||||
&state,
|
||||
parent_root,
|
||||
proposer_index,
|
||||
builder_params,
|
||||
builder_boost_factor,
|
||||
block_production_version,
|
||||
)?;
|
||||
Some(prepare_payload_handle)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (mut proposer_slashings, mut attester_slashings, mut voluntary_exits) =
|
||||
@@ -5751,6 +5747,48 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
execution_payload_value,
|
||||
)
|
||||
}
|
||||
BeaconState::Fulu(_) => {
|
||||
let (
|
||||
payload,
|
||||
kzg_commitments,
|
||||
maybe_blobs_and_proofs,
|
||||
maybe_requests,
|
||||
execution_payload_value,
|
||||
) = block_contents
|
||||
.ok_or(BlockProductionError::MissingExecutionPayload)?
|
||||
.deconstruct();
|
||||
|
||||
(
|
||||
BeaconBlock::Fulu(BeaconBlockFulu {
|
||||
slot,
|
||||
proposer_index,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBodyFulu {
|
||||
randao_reveal,
|
||||
eth1_data,
|
||||
graffiti,
|
||||
proposer_slashings: proposer_slashings.into(),
|
||||
attester_slashings: attester_slashings_electra.into(),
|
||||
attestations: attestations_electra.into(),
|
||||
deposits: deposits.into(),
|
||||
voluntary_exits: voluntary_exits.into(),
|
||||
sync_aggregate: sync_aggregate
|
||||
.ok_or(BlockProductionError::MissingSyncAggregate)?,
|
||||
execution_payload: payload
|
||||
.try_into()
|
||||
.map_err(|_| BlockProductionError::InvalidPayloadFork)?,
|
||||
bls_to_execution_changes: bls_to_execution_changes.into(),
|
||||
blob_kzg_commitments: kzg_commitments
|
||||
.ok_or(BlockProductionError::InvalidPayloadFork)?,
|
||||
execution_requests: maybe_requests
|
||||
.ok_or(BlockProductionError::MissingExecutionRequests)?,
|
||||
},
|
||||
}),
|
||||
maybe_blobs_and_proofs,
|
||||
execution_payload_value,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let block = SignedBeaconBlock::from_block(
|
||||
|
||||
@@ -374,19 +374,15 @@ pub fn get_execution_payload<T: BeaconChainTypes>(
|
||||
let latest_execution_payload_header = state.latest_execution_payload_header()?;
|
||||
let latest_execution_payload_header_block_hash = latest_execution_payload_header.block_hash();
|
||||
let latest_execution_payload_header_gas_limit = latest_execution_payload_header.gas_limit();
|
||||
let withdrawals = match state {
|
||||
&BeaconState::Capella(_) | &BeaconState::Deneb(_) | &BeaconState::Electra(_) => {
|
||||
Some(get_expected_withdrawals(state, spec)?.0.into())
|
||||
}
|
||||
&BeaconState::Bellatrix(_) => None,
|
||||
// These shouldn't happen but they're here to make the pattern irrefutable
|
||||
&BeaconState::Base(_) | &BeaconState::Altair(_) => None,
|
||||
let withdrawals = if state.fork_name_unchecked().capella_enabled() {
|
||||
Some(get_expected_withdrawals(state, spec)?.0.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let parent_beacon_block_root = match state {
|
||||
BeaconState::Deneb(_) | BeaconState::Electra(_) => Some(parent_block_root),
|
||||
BeaconState::Bellatrix(_) | BeaconState::Capella(_) => None,
|
||||
// These shouldn't happen but they're here to make the pattern irrefutable
|
||||
BeaconState::Base(_) | BeaconState::Altair(_) => None,
|
||||
let parent_beacon_block_root = if state.fork_name_unchecked().deneb_enabled() {
|
||||
Some(parent_block_root)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Spawn a task to obtain the execution payload from the EL via a series of async calls. The
|
||||
|
||||
114
beacon_node/beacon_chain/src/fulu_readiness.rs
Normal file
114
beacon_node/beacon_chain/src/fulu_readiness.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
//! Provides tools for checking if a node is ready for the Fulu upgrade.
|
||||
|
||||
use crate::{BeaconChain, BeaconChainTypes};
|
||||
use execution_layer::http::{ENGINE_GET_PAYLOAD_V5, ENGINE_NEW_PAYLOAD_V5};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
use types::*;
|
||||
|
||||
/// The time before the Fulu fork when we will start issuing warnings about preparation.
|
||||
use super::bellatrix_readiness::SECONDS_IN_A_WEEK;
|
||||
pub const FULU_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 FuluReadiness {
|
||||
/// The execution engine is fulu-enabled (as far as we can tell)
|
||||
Ready,
|
||||
/// We are connected to an execution engine which doesn't support the V5 engine api methods
|
||||
V5MethodsNotSupported { 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 FuluReadiness {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
FuluReadiness::Ready => {
|
||||
write!(f, "This node appears ready for Fulu.")
|
||||
}
|
||||
FuluReadiness::ExchangeCapabilitiesFailed { error } => write!(
|
||||
f,
|
||||
"Could not exchange capabilities with the \
|
||||
execution endpoint: {}",
|
||||
error
|
||||
),
|
||||
FuluReadiness::NoExecutionEndpoint => write!(
|
||||
f,
|
||||
"The --execution-endpoint flag is not specified, this is a \
|
||||
requirement post-merge"
|
||||
),
|
||||
FuluReadiness::V5MethodsNotSupported { error } => write!(
|
||||
f,
|
||||
"Execution endpoint does not support Fulu methods: {}",
|
||||
error
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Returns `true` if fulu epoch is set and Fulu fork has occurred or will
|
||||
/// occur within `FULU_READINESS_PREPARATION_SECONDS`
|
||||
pub fn is_time_to_prepare_for_fulu(&self, current_slot: Slot) -> bool {
|
||||
if let Some(fulu_epoch) = self.spec.fulu_fork_epoch {
|
||||
let fulu_slot = fulu_epoch.start_slot(T::EthSpec::slots_per_epoch());
|
||||
let fulu_readiness_preparation_slots =
|
||||
FULU_READINESS_PREPARATION_SECONDS / self.spec.seconds_per_slot;
|
||||
// Return `true` if Fulu has happened or is within the preparation time.
|
||||
current_slot + fulu_readiness_preparation_slots > fulu_slot
|
||||
} else {
|
||||
// The Fulu 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 fulu.
|
||||
pub async fn check_fulu_readiness(&self) -> FuluReadiness {
|
||||
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
|
||||
FuluReadiness::ExchangeCapabilitiesFailed {
|
||||
error: format!("{:?}", e),
|
||||
}
|
||||
}
|
||||
Ok(capabilities) => {
|
||||
let mut missing_methods = String::from("Required Methods Unsupported:");
|
||||
let mut all_good = true;
|
||||
if !capabilities.get_payload_v5 {
|
||||
missing_methods.push(' ');
|
||||
missing_methods.push_str(ENGINE_GET_PAYLOAD_V5);
|
||||
all_good = false;
|
||||
}
|
||||
if !capabilities.new_payload_v5 {
|
||||
missing_methods.push(' ');
|
||||
missing_methods.push_str(ENGINE_NEW_PAYLOAD_V5);
|
||||
all_good = false;
|
||||
}
|
||||
|
||||
if all_good {
|
||||
FuluReadiness::Ready
|
||||
} else {
|
||||
FuluReadiness::V5MethodsNotSupported {
|
||||
error: missing_methods,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FuluReadiness::NoExecutionEndpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ pub mod execution_payload;
|
||||
pub mod fetch_blobs;
|
||||
pub mod fork_choice_signal;
|
||||
pub mod fork_revert;
|
||||
pub mod fulu_readiness;
|
||||
pub mod graffiti_calculator;
|
||||
mod head_tracker;
|
||||
pub mod historical_blocks;
|
||||
|
||||
@@ -501,6 +501,9 @@ where
|
||||
spec.electra_fork_epoch.map(|epoch| {
|
||||
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
|
||||
});
|
||||
mock.server.execution_block_generator().osaka_time = spec.fulu_fork_epoch.map(|epoch| {
|
||||
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
@@ -623,6 +626,9 @@ pub fn mock_execution_layer_from_parts<E: EthSpec>(
|
||||
let prague_time = spec.electra_fork_epoch.map(|epoch| {
|
||||
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
|
||||
});
|
||||
let osaka_time = spec.fulu_fork_epoch.map(|epoch| {
|
||||
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
|
||||
});
|
||||
|
||||
let kzg = get_kzg(spec);
|
||||
|
||||
@@ -632,6 +638,7 @@ pub fn mock_execution_layer_from_parts<E: EthSpec>(
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
osaka_time,
|
||||
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
|
||||
spec.clone(),
|
||||
Some(kzg),
|
||||
@@ -913,15 +920,12 @@ where
|
||||
&self.spec,
|
||||
));
|
||||
|
||||
let block_contents: SignedBlockContentsTuple<E> = match *signed_block {
|
||||
SignedBeaconBlock::Base(_)
|
||||
| SignedBeaconBlock::Altair(_)
|
||||
| SignedBeaconBlock::Bellatrix(_)
|
||||
| SignedBeaconBlock::Capella(_) => (signed_block, None),
|
||||
SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => {
|
||||
let block_contents: SignedBlockContentsTuple<E> =
|
||||
if signed_block.fork_name_unchecked().deneb_enabled() {
|
||||
(signed_block, block_response.blob_items)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
(signed_block, None)
|
||||
};
|
||||
|
||||
(block_contents, block_response.state)
|
||||
}
|
||||
@@ -977,15 +981,12 @@ where
|
||||
&self.spec,
|
||||
));
|
||||
|
||||
let block_contents: SignedBlockContentsTuple<E> = match *signed_block {
|
||||
SignedBeaconBlock::Base(_)
|
||||
| SignedBeaconBlock::Altair(_)
|
||||
| SignedBeaconBlock::Bellatrix(_)
|
||||
| SignedBeaconBlock::Capella(_) => (signed_block, None),
|
||||
SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => {
|
||||
let block_contents: SignedBlockContentsTuple<E> =
|
||||
if signed_block.fork_name_unchecked().deneb_enabled() {
|
||||
(signed_block, block_response.blob_items)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
(signed_block, None)
|
||||
};
|
||||
(block_contents, pre_state)
|
||||
}
|
||||
|
||||
@@ -2863,6 +2864,25 @@ pub fn generate_rand_block_and_blobs<E: EthSpec>(
|
||||
message.body.blob_kzg_commitments = bundle.commitments.clone();
|
||||
bundle
|
||||
}
|
||||
SignedBeaconBlock::Fulu(SignedBeaconBlockFulu {
|
||||
ref mut message, ..
|
||||
}) => {
|
||||
// Get either zero blobs or a random number of blobs between 1 and Max Blobs.
|
||||
let payload: &mut FullPayloadFulu<E> = &mut message.body.execution_payload;
|
||||
let num_blobs = match num_blobs {
|
||||
NumBlobs::Random => rng.gen_range(1..=E::max_blobs_per_block()),
|
||||
NumBlobs::Number(n) => n,
|
||||
NumBlobs::None => 0,
|
||||
};
|
||||
let (bundle, transactions) =
|
||||
execution_layer::test_utils::generate_blobs::<E>(num_blobs).unwrap();
|
||||
payload.execution_payload.transactions = <_>::default();
|
||||
for tx in Vec::from(transactions) {
|
||||
payload.execution_payload.transactions.push(tx).unwrap();
|
||||
}
|
||||
message.body.blob_kzg_commitments = bundle.commitments.clone();
|
||||
bundle
|
||||
}
|
||||
_ => return (block, blob_sidecars),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user