Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Fulu boilerplate #6695

Open
wants to merge 4 commits into
base: unstable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ PROFILE ?= release

# List of all hard forks. This list is used to set env variables for several tests so that
# they run for different forks.
FORKS=phase0 altair bellatrix capella deneb electra
FORKS=phase0 altair bellatrix capella deneb electra fulu

# Extra flags for Cargo
CARGO_INSTALL_EXTRA_FLAGS?=
Expand Down
3 changes: 2 additions & 1 deletion beacon_node/beacon_chain/src/attestation_rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
| BeaconState::Bellatrix(_)
| BeaconState::Capella(_)
| BeaconState::Deneb(_)
| BeaconState::Electra(_) => self.compute_attestation_rewards_altair(state, validators),
| BeaconState::Electra(_)
| BeaconState::Fulu(_) => self.compute_attestation_rewards_altair(state, validators),
}
}

Expand Down
5 changes: 4 additions & 1 deletion beacon_node/beacon_chain/src/beacon_block_streamer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use types::{
};
use types::{
ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadElectra,
ExecutionPayloadHeader,
ExecutionPayloadFulu, ExecutionPayloadHeader,
};

#[derive(PartialEq)]
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -749,6 +750,7 @@ mod tests {
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>();
Expand All @@ -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());
Expand Down
45 changes: 44 additions & 1 deletion beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5322,7 +5322,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
BeaconState::Bellatrix(_)
| BeaconState::Capella(_)
| BeaconState::Deneb(_)
| BeaconState::Electra(_) => {
| BeaconState::Electra(_)
| BeaconState::Fulu(_) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do fork.after_bellatrix here? the argument of using a match to force us to think about the fork is broken here, because we don't know what EIPs will exist in the fork and if we need a new handler. So now we are getting the worst of both worlds. a 1980 lines boilerplate PR and no guarantees or correctness

let prepare_payload_handle = get_execution_payload(
self.clone(),
&state,
Expand Down Expand Up @@ -5751,6 +5752,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(
Expand Down
11 changes: 7 additions & 4 deletions beacon_node/beacon_chain/src/execution_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,15 +379,18 @@ pub fn get_execution_payload<T: BeaconChainTypes>(
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::Capella(_)
| &BeaconState::Deneb(_)
| &BeaconState::Electra(_)
| &BeaconState::Fulu(_) => 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 parent_beacon_block_root = match state {
BeaconState::Deneb(_) | BeaconState::Electra(_) => Some(parent_block_root),
BeaconState::Deneb(_) | BeaconState::Electra(_) | BeaconState::Fulu(_) => {
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,
Expand Down
114 changes: 114 additions & 0 deletions beacon_node/beacon_chain/src/fulu_readiness.rs
Original file line number Diff line number Diff line change
@@ -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 V4 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
}
}
}
1 change: 1 addition & 0 deletions beacon_node/beacon_chain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
38 changes: 32 additions & 6 deletions beacon_node/beacon_chain/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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);

Expand All @@ -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),
Expand Down Expand Up @@ -918,9 +925,9 @@ where
| SignedBeaconBlock::Altair(_)
| SignedBeaconBlock::Bellatrix(_)
| SignedBeaconBlock::Capella(_) => (signed_block, None),
SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => {
(signed_block, block_response.blob_items)
}
SignedBeaconBlock::Deneb(_)
| SignedBeaconBlock::Electra(_)
| SignedBeaconBlock::Fulu(_) => (signed_block, block_response.blob_items),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be .after_deneb()

};

(block_contents, block_response.state)
Expand Down Expand Up @@ -982,9 +989,9 @@ where
| SignedBeaconBlock::Altair(_)
| SignedBeaconBlock::Bellatrix(_)
| SignedBeaconBlock::Capella(_) => (signed_block, None),
SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => {
(signed_block, block_response.blob_items)
}
SignedBeaconBlock::Deneb(_)
| SignedBeaconBlock::Electra(_)
| SignedBeaconBlock::Fulu(_) => (signed_block, block_response.blob_items),
};
(block_contents, pre_state)
}
Expand Down Expand Up @@ -2863,6 +2870,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),
};

Expand Down
9 changes: 9 additions & 0 deletions beacon_node/beacon_chain/tests/block_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,11 @@ async fn invalid_signature_attester_slashing() {
.push(attester_slashing.as_electra().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Fulu(ref mut blk) => {
blk.attester_slashings
.push(attester_slashing.as_electra().unwrap().clone())
.expect("should update attester slashing");
}
}
snapshots[block_index].beacon_block =
Arc::new(SignedBeaconBlock::from_block(block, signature));
Expand Down Expand Up @@ -809,6 +814,10 @@ async fn invalid_signature_attestation() {
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Fulu(ref mut blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
};

if block.body().attestations_len() > 0 {
Expand Down
1 change: 1 addition & 0 deletions beacon_node/beacon_chain/tests/validator_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ async fn produces_missed_blocks() {
ForkName::Capella => 11,
ForkName::Deneb => 3,
ForkName::Electra => 1,
ForkName::Fulu => 5,
};

let harness2 = get_harness(validator_count, vec![validator_index_to_monitor]);
Expand Down
Loading
Loading