diff --git a/Makefile b/Makefile index 5fb8a666db9..66d0b682689 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,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 fulu +FORKS=phase0 altair bellatrix capella deneb electra fulu gloas # List of all recent hard forks. This list is used to set env variables for http_api tests RECENT_FORKS=electra fulu diff --git a/beacon_node/beacon_chain/src/beacon_block_streamer.rs b/beacon_node/beacon_chain/src/beacon_block_streamer.rs index 3e52dc7a7d9..d4ce38927b2 100644 --- a/beacon_node/beacon_chain/src/beacon_block_streamer.rs +++ b/beacon_node/beacon_chain/src/beacon_block_streamer.rs @@ -16,7 +16,7 @@ use types::{ }; use types::{ ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadElectra, - ExecutionPayloadFulu, ExecutionPayloadHeader, + ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionPayloadHeader, }; #[derive(PartialEq)] @@ -101,6 +101,7 @@ fn reconstruct_default_header_block( ForkName::Deneb => ExecutionPayloadDeneb::default().into(), ForkName::Electra => ExecutionPayloadElectra::default().into(), ForkName::Fulu => ExecutionPayloadFulu::default().into(), + ForkName::Gloas => ExecutionPayloadGloas::default().into(), ForkName::Base | ForkName::Altair => { return Err(Error::PayloadReconstruction(format!( "Block with fork variant {} has execution payload", @@ -715,7 +716,7 @@ mod tests { } #[tokio::test] - async fn check_all_blocks_from_altair_to_fulu() { + async fn check_all_blocks_from_altair_to_gloas() { let slots_per_epoch = MinimalEthSpec::slots_per_epoch() as usize; let num_epochs = 12; let bellatrix_fork_epoch = 2usize; @@ -723,6 +724,7 @@ mod tests { let deneb_fork_epoch = 6usize; let electra_fork_epoch = 8usize; let fulu_fork_epoch = 10usize; + let gloas_fork_epoch = 12usize; let num_blocks_produced = num_epochs * slots_per_epoch; let mut spec = test_spec::(); @@ -732,6 +734,7 @@ mod tests { 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)); + spec.gloas_fork_epoch = Some(Epoch::new(gloas_fork_epoch as u64)); let spec = Arc::new(spec); let harness = get_harness(VALIDATOR_COUNT, spec.clone()); diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 524fd1e0999..d5a2929301e 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -5726,6 +5726,48 @@ impl BeaconChain { execution_payload_value, ) } + BeaconState::Gloas(_) => { + let ( + payload, + kzg_commitments, + maybe_blobs_and_proofs, + maybe_requests, + execution_payload_value, + ) = block_contents + .ok_or(BlockProductionError::MissingExecutionPayload)? + .deconstruct(); + + ( + BeaconBlock::Gloas(BeaconBlockGloas { + slot, + proposer_index, + parent_root, + state_root: Hash256::zero(), + body: BeaconBlockBodyGloas { + 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( diff --git a/beacon_node/beacon_chain/src/capella_readiness.rs b/beacon_node/beacon_chain/src/capella_readiness.rs deleted file mode 100644 index 88af7db0aaa..00000000000 --- a/beacon_node/beacon_chain/src/capella_readiness.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Provides tools for checking if a node is ready for the Capella upgrade. - -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::bellatrix_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 BeaconChain { - /// 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 - } - } -} diff --git a/beacon_node/beacon_chain/src/deneb_readiness.rs b/beacon_node/beacon_chain/src/deneb_readiness.rs deleted file mode 100644 index e11070a1f4f..00000000000 --- a/beacon_node/beacon_chain/src/deneb_readiness.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Provides tools for checking if a node is ready for the Deneb upgrade. - -use crate::{BeaconChain, BeaconChainTypes}; -use execution_layer::http::{ - ENGINE_FORKCHOICE_UPDATED_V3, ENGINE_GET_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V3, -}; -use serde::{Deserialize, Serialize}; -use std::fmt; -use std::time::Duration; -use types::*; - -/// The time before the Deneb fork when we will start issuing warnings about preparation. -use super::bellatrix_readiness::SECONDS_IN_A_WEEK; -pub const DENEB_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 DenebReadiness { - /// The execution engine is deneb-enabled (as far as we can tell) - Ready, - /// We are connected to an execution engine which doesn't support the V3 engine api methods - V3MethodsNotSupported { 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 DenebReadiness { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - DenebReadiness::Ready => { - write!(f, "This node appears ready for Deneb.") - } - DenebReadiness::ExchangeCapabilitiesFailed { error } => write!( - f, - "Could not exchange capabilities with the \ - execution endpoint: {}", - error - ), - DenebReadiness::NoExecutionEndpoint => write!( - f, - "The --execution-endpoint flag is not specified, this is a \ - requirement post-merge" - ), - DenebReadiness::V3MethodsNotSupported { error } => write!( - f, - "Execution endpoint does not support Deneb methods: {}", - error - ), - } - } -} - -impl BeaconChain { - /// Returns `true` if deneb epoch is set and Deneb fork has occurred or will - /// occur within `DENEB_READINESS_PREPARATION_SECONDS` - pub fn is_time_to_prepare_for_deneb(&self, current_slot: Slot) -> bool { - if let Some(deneb_epoch) = self.spec.deneb_fork_epoch { - let deneb_slot = deneb_epoch.start_slot(T::EthSpec::slots_per_epoch()); - let deneb_readiness_preparation_slots = - DENEB_READINESS_PREPARATION_SECONDS / self.spec.seconds_per_slot; - // Return `true` if Deneb has happened or is within the preparation time. - current_slot + deneb_readiness_preparation_slots > deneb_slot - } else { - // The Deneb 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_deneb_readiness(&self) -> DenebReadiness { - 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 - DenebReadiness::ExchangeCapabilitiesFailed { - error: format!("{:?}", e), - } - } - Ok(capabilities) => { - let mut missing_methods = String::from("Required Methods Unsupported:"); - let mut all_good = true; - if !capabilities.get_payload_v3 { - missing_methods.push(' '); - missing_methods.push_str(ENGINE_GET_PAYLOAD_V3); - all_good = false; - } - if !capabilities.forkchoice_updated_v3 { - missing_methods.push(' '); - missing_methods.push_str(ENGINE_FORKCHOICE_UPDATED_V3); - all_good = false; - } - if !capabilities.new_payload_v3 { - missing_methods.push(' '); - missing_methods.push_str(ENGINE_NEW_PAYLOAD_V3); - all_good = false; - } - - if all_good { - DenebReadiness::Ready - } else { - DenebReadiness::V3MethodsNotSupported { - error: missing_methods, - } - } - } - } - } else { - DenebReadiness::NoExecutionEndpoint - } - } -} diff --git a/beacon_node/beacon_chain/src/electra_readiness.rs b/beacon_node/beacon_chain/src/electra_readiness.rs deleted file mode 100644 index 551d43f9fd6..00000000000 --- a/beacon_node/beacon_chain/src/electra_readiness.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! Provides tools for checking if a node is ready for the Electra upgrade and following merge -//! transition. - -use crate::{BeaconChain, BeaconChainTypes}; -use execution_layer::http::{ENGINE_GET_PAYLOAD_V4, ENGINE_NEW_PAYLOAD_V4}; -use serde::{Deserialize, Serialize}; -use std::fmt; -use std::time::Duration; -use types::*; - -/// The time before the Electra fork when we will start issuing warnings about preparation. -use super::bellatrix_readiness::SECONDS_IN_A_WEEK; -pub const ELECTRA_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 ElectraReadiness { - /// The execution engine is electra-enabled (as far as we can tell) - Ready, - /// We are connected to an execution engine which doesn't support the V4 engine api methods - V4MethodsNotSupported { 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 ElectraReadiness { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ElectraReadiness::Ready => { - write!(f, "This node appears ready for Electra.") - } - ElectraReadiness::ExchangeCapabilitiesFailed { error } => write!( - f, - "Could not exchange capabilities with the \ - execution endpoint: {}", - error - ), - ElectraReadiness::NoExecutionEndpoint => write!( - f, - "The --execution-endpoint flag is not specified, this is a \ - requirement post-merge" - ), - ElectraReadiness::V4MethodsNotSupported { error } => write!( - f, - "Execution endpoint does not support Electra methods: {}", - error - ), - } - } -} - -impl BeaconChain { - /// Returns `true` if electra epoch is set and Electra fork has occurred or will - /// occur within `ELECTRA_READINESS_PREPARATION_SECONDS` - pub fn is_time_to_prepare_for_electra(&self, current_slot: Slot) -> bool { - if let Some(electra_epoch) = self.spec.electra_fork_epoch { - let electra_slot = electra_epoch.start_slot(T::EthSpec::slots_per_epoch()); - let electra_readiness_preparation_slots = - ELECTRA_READINESS_PREPARATION_SECONDS / self.spec.seconds_per_slot; - // Return `true` if Electra has happened or is within the preparation time. - current_slot + electra_readiness_preparation_slots > electra_slot - } else { - // The Electra 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 electra. - pub async fn check_electra_readiness(&self) -> ElectraReadiness { - 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 - ElectraReadiness::ExchangeCapabilitiesFailed { - error: format!("{:?}", e), - } - } - Ok(capabilities) => { - let mut missing_methods = String::from("Required Methods Unsupported:"); - let mut all_good = true; - if !capabilities.get_payload_v4 { - missing_methods.push(' '); - missing_methods.push_str(ENGINE_GET_PAYLOAD_V4); - all_good = false; - } - if !capabilities.new_payload_v4 { - missing_methods.push(' '); - missing_methods.push_str(ENGINE_NEW_PAYLOAD_V4); - all_good = false; - } - - if all_good { - ElectraReadiness::Ready - } else { - ElectraReadiness::V4MethodsNotSupported { - error: missing_methods, - } - } - } - } - } else { - ElectraReadiness::NoExecutionEndpoint - } - } -} diff --git a/beacon_node/beacon_chain/src/fulu_readiness.rs b/beacon_node/beacon_chain/src/fulu_readiness.rs deleted file mode 100644 index 0c17bdde9ed..00000000000 --- a/beacon_node/beacon_chain/src/fulu_readiness.rs +++ /dev/null @@ -1,114 +0,0 @@ -//! 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_V4}; -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 BeaconChain { - /// 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_v4 { - missing_methods.push(' '); - missing_methods.push_str(ENGINE_NEW_PAYLOAD_V4); - all_good = false; - } - - if all_good { - FuluReadiness::Ready - } else { - FuluReadiness::V5MethodsNotSupported { - error: missing_methods, - } - } - } - } - } else { - FuluReadiness::NoExecutionEndpoint - } - } -} diff --git a/beacon_node/beacon_chain/src/lib.rs b/beacon_node/beacon_chain/src/lib.rs index c755eeb9f44..9d8c3dba38f 100644 --- a/beacon_node/beacon_chain/src/lib.rs +++ b/beacon_node/beacon_chain/src/lib.rs @@ -16,20 +16,16 @@ mod block_verification; pub mod block_verification_types; pub mod builder; pub mod canonical_head; -pub mod capella_readiness; pub mod chain_config; pub mod data_availability_checker; pub mod data_column_verification; -pub mod deneb_readiness; mod early_attester_cache; -pub mod electra_readiness; mod errors; pub mod events; 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; pub mod historical_blocks; pub mod kzg_utils; diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 8e45ec88b4d..c9b84f25b8b 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -496,6 +496,10 @@ where 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() }); + mock.server.execution_block_generator().amsterdam_time = + spec.gloas_fork_epoch.map(|epoch| { + genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64() + }); self } @@ -630,6 +634,9 @@ pub fn mock_execution_layer_from_parts( let osaka_time = spec.fulu_fork_epoch.map(|epoch| { HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64() }); + let amsterdam_time = spec.gloas_fork_epoch.map(|epoch| { + HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64() + }); let kzg = get_kzg(&spec); @@ -640,6 +647,7 @@ pub fn mock_execution_layer_from_parts( cancun_time, prague_time, osaka_time, + amsterdam_time, Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()), spec, Some(kzg), @@ -3244,6 +3252,25 @@ pub fn generate_rand_block_and_blobs( message.body.blob_kzg_commitments = bundle.commitments.clone(); bundle } + SignedBeaconBlock::Gloas(SignedBeaconBlockGloas { + ref mut message, .. + }) => { + // Get either zero blobs or a random number of blobs between 1 and Max Blobs. + let payload: &mut FullPayloadGloas = &mut message.body.execution_payload; + let num_blobs = match num_blobs { + NumBlobs::Random => rng.random_range(1..=max_blobs), + NumBlobs::Number(n) => n, + NumBlobs::None => 0, + }; + let (bundle, transactions) = + execution_layer::test_utils::generate_blobs::(num_blobs, fork_name).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), }; diff --git a/beacon_node/beacon_chain/tests/block_verification.rs b/beacon_node/beacon_chain/tests/block_verification.rs index a93dda1080a..58ca4a032ed 100644 --- a/beacon_node/beacon_chain/tests/block_verification.rs +++ b/beacon_node/beacon_chain/tests/block_verification.rs @@ -788,6 +788,11 @@ async fn invalid_signature_attester_slashing() { .push(attester_slashing.as_electra().unwrap().clone()) .expect("should update attester slashing"); } + BeaconBlockBodyRefMut::Gloas(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)); @@ -847,6 +852,10 @@ async fn invalid_signature_attestation() { .attestations .get_mut(0) .map(|att| att.signature = junk_aggregate_signature()), + BeaconBlockBodyRefMut::Gloas(blk) => blk + .attestations + .get_mut(0) + .map(|att| att.signature = junk_aggregate_signature()), }; if block.body().attestations_len() > 0 { diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index 2a2f1d7d059..fbb592b510f 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -184,6 +184,7 @@ async fn light_client_bootstrap_test() { LightClientBootstrap::Deneb(lc_bootstrap) => lc_bootstrap.header.beacon.slot, LightClientBootstrap::Electra(lc_bootstrap) => lc_bootstrap.header.beacon.slot, LightClientBootstrap::Fulu(lc_bootstrap) => lc_bootstrap.header.beacon.slot, + LightClientBootstrap::Gloas(lc_bootstrap) => lc_bootstrap.header.beacon.slot, }; assert_eq!( diff --git a/beacon_node/client/src/notifier.rs b/beacon_node/client/src/notifier.rs index e1c5bd2293c..1e58c210daa 100644 --- a/beacon_node/client/src/notifier.rs +++ b/beacon_node/client/src/notifier.rs @@ -1,11 +1,17 @@ use crate::metrics; use beacon_chain::{ BeaconChain, BeaconChainTypes, ExecutionStatus, - bellatrix_readiness::{BellatrixReadiness, GenesisExecutionPayloadStatus, MergeConfig}, - capella_readiness::CapellaReadiness, - deneb_readiness::DenebReadiness, - electra_readiness::ElectraReadiness, - fulu_readiness::FuluReadiness, + bellatrix_readiness::{ + BellatrixReadiness, GenesisExecutionPayloadStatus, MergeConfig, SECONDS_IN_A_WEEK, + }, +}; +use execution_layer::{ + EngineCapabilities, + http::{ + ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3, ENGINE_GET_PAYLOAD_V2, + ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, + ENGINE_NEW_PAYLOAD_V4, + }, }; use lighthouse_network::{NetworkGlobals, types::SyncState}; use logging::crit; @@ -30,6 +36,9 @@ const SPEEDO_OBSERVATIONS: usize = 4; /// The number of slots between logs that give detail about backfill process. const BACKFILL_LOG_INTERVAL: u64 = 5; +pub const FORK_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK * 2; +pub const ENGINE_CAPABILITIES_REFRESH_INTERVAL: u64 = 300; + /// Spawns a notifier service which periodically logs information about the node. pub fn spawn_notifier( executor: task_executor::TaskExecutor, @@ -61,7 +70,7 @@ pub fn spawn_notifier( "Waiting for genesis" ); bellatrix_readiness_logging(Slot::new(0), &beacon_chain).await; - capella_readiness_logging(Slot::new(0), &beacon_chain).await; + post_bellatrix_readiness_logging(Slot::new(0), &beacon_chain).await; genesis_execution_payload_logging(&beacon_chain).await; sleep(slot_duration).await; } @@ -309,10 +318,7 @@ pub fn spawn_notifier( } bellatrix_readiness_logging(current_slot, &beacon_chain).await; - capella_readiness_logging(current_slot, &beacon_chain).await; - deneb_readiness_logging(current_slot, &beacon_chain).await; - electra_readiness_logging(current_slot, &beacon_chain).await; - fulu_readiness_logging(current_slot, &beacon_chain).await; + post_bellatrix_readiness_logging(current_slot, &beacon_chain).await; } }; @@ -346,18 +352,6 @@ async fn bellatrix_readiness_logging( return; } - if merge_completed && !has_execution_layer { - // Logging of the EE being offline is handled in the other readiness logging functions. - if !beacon_chain.is_time_to_prepare_for_capella(current_slot) { - error!( - info = "you need an execution engine to validate blocks, see: \ - https://lighthouse-book.sigmaprime.io/archived_merge_migration.html", - "Execution endpoint required" - ); - } - return; - } - match beacon_chain.check_bellatrix_readiness(current_slot).await { BellatrixReadiness::Ready { config, @@ -406,213 +400,149 @@ async fn bellatrix_readiness_logging( } /// Provides some helpful logging to users to indicate if their node is ready for Capella -async fn capella_readiness_logging( +async fn post_bellatrix_readiness_logging( current_slot: Slot, beacon_chain: &BeaconChain, ) { - let capella_completed = beacon_chain - .canonical_head - .cached_head() - .snapshot - .beacon_state - .fork_name_unchecked() - .capella_enabled(); - - let has_execution_layer = beacon_chain.execution_layer.is_some(); - - if capella_completed && has_execution_layer - || !beacon_chain.is_time_to_prepare_for_capella(current_slot) - { - return; - } + if let Some(fork) = find_next_fork_to_prepare(current_slot, beacon_chain) { + let readiness = if let Some(el) = beacon_chain.execution_layer.as_ref() { + match el + .get_engine_capabilities(Some(Duration::from_secs( + ENGINE_CAPABILITIES_REFRESH_INTERVAL, + ))) + .await + { + Err(e) => Err(format!("Exchange capabilities failed: {e:?}")), + Ok(capabilities) => { + let missing_methods = methods_required_for_fork(fork, capabilities); + if missing_methods.is_empty() { + Ok(()) + } else { + Err(format!("Missing required methods: {missing_methods:?}")) + } + } + } + } else { + Err("No execution endpoint".to_string()) + }; - if capella_completed && !has_execution_layer { - // Logging of the EE being offline is handled in the other readiness logging functions. - if !beacon_chain.is_time_to_prepare_for_deneb(current_slot) { - error!( - info = "you need a Capella enabled execution engine to validate blocks, see: \ - https://lighthouse-book.sigmaprime.io/archived_merge_migration.html", - "Execution endpoint required" + if let Err(readiness) = readiness { + warn!( + info = %readiness, + "Not ready for {}", fork ); - } - return; - } - - match beacon_chain.check_capella_readiness().await { - CapellaReadiness::Ready => { + } else { info!( - info = "ensure the execution endpoint is updated to the latest Capella/Shanghai release", - "Ready for Capella" + info = "ensure the execution endpoint is updated to the latest release", + "Ready for {}", fork ) } - readiness @ CapellaReadiness::ExchangeCapabilitiesFailed { error: _ } => { - error!( - hint = "the execution endpoint may be offline", - info = %readiness, - "Not ready for Capella" - ) - } - readiness => warn!( - hint = "try updating the execution endpoint", - info = %readiness, - "Not ready for Capella" - ), } } -/// Provides some helpful logging to users to indicate if their node is ready for Deneb -async fn deneb_readiness_logging( +fn find_next_fork_to_prepare( current_slot: Slot, beacon_chain: &BeaconChain, -) { - let deneb_completed = beacon_chain +) -> Option { + let head_fork = beacon_chain .canonical_head .cached_head() .snapshot .beacon_state - .fork_name_unchecked() - .deneb_enabled(); + .fork_name_unchecked(); - let has_execution_layer = beacon_chain.execution_layer.is_some(); - - if deneb_completed && has_execution_layer - || !beacon_chain.is_time_to_prepare_for_deneb(current_slot) + // Iterate forks from latest to oldest + for (fork, fork_epoch) in ForkName::list_all_fork_epochs(&beacon_chain.spec) + .iter() + .rev() { - return; - } - - if deneb_completed && !has_execution_layer { - error!( - info = "you need a Deneb enabled execution engine to validate blocks.", - "Execution endpoint required" - ); - return; - } - - match beacon_chain.check_deneb_readiness().await { - DenebReadiness::Ready => { - info!( - info = - "ensure the execution endpoint is updated to the latest Deneb/Cancun release", - "Ready for Deneb" - ) - } - readiness @ DenebReadiness::ExchangeCapabilitiesFailed { error: _ } => { - error!( - hint = "the execution endpoint may be offline", - info = %readiness, - "Not ready for Deneb" - ) + // This readiness only handles capella and post fork + if *fork <= ForkName::Bellatrix { + break; } - readiness => warn!( - hint = "try updating the execution endpoint", - info = %readiness, - "Not ready for Deneb" - ), - } -} -/// Provides some helpful logging to users to indicate if their node is ready for Electra. -async fn electra_readiness_logging( - current_slot: Slot, - beacon_chain: &BeaconChain, -) { - let electra_completed = beacon_chain - .canonical_head - .cached_head() - .snapshot - .beacon_state - .fork_name_unchecked() - .electra_enabled(); - let has_execution_layer = beacon_chain.execution_layer.is_some(); + // head state has already activated this fork + if head_fork >= *fork { + break; + } - if electra_completed && has_execution_layer - || !beacon_chain.is_time_to_prepare_for_electra(current_slot) - { - return; + // Find the first fork that is scheduled and close to happen + if let Some(fork_epoch) = fork_epoch { + let fork_slot = fork_epoch.start_slot(T::EthSpec::slots_per_epoch()); + let preparation_slots = + FORK_READINESS_PREPARATION_SECONDS / beacon_chain.spec.seconds_per_slot; + let in_fork_preparation_period = current_slot + preparation_slots > fork_slot; + if in_fork_preparation_period { + return Some(*fork); + } + } } - if electra_completed && !has_execution_layer { - // When adding a new fork, add a check for the next fork readiness here. - error!( - info = "you need a Electra enabled execution engine to validate blocks.", - "Execution endpoint required" - ); - return; - } + None +} - match beacon_chain.check_electra_readiness().await { - ElectraReadiness::Ready => { - info!( - info = - "ensure the execution endpoint is updated to the latest Electra/Prague release", - "Ready for Electra" - ) +fn methods_required_for_fork( + fork: ForkName, + capabilities: EngineCapabilities, +) -> Vec<&'static str> { + let mut missing_methods = vec![]; + match fork { + ForkName::Base | ForkName::Altair | ForkName::Bellatrix => { + warn!( + fork = %fork, + "Invalid methods_required_for_fork call" + ); } - readiness @ ElectraReadiness::ExchangeCapabilitiesFailed { error: _ } => { - error!( - hint = "the execution endpoint may be offline", - info = %readiness, - "Not ready for Electra" - ) + ForkName::Capella => { + if !capabilities.get_payload_v2 { + missing_methods.push(ENGINE_GET_PAYLOAD_V2); + } + if !capabilities.forkchoice_updated_v2 { + missing_methods.push(ENGINE_FORKCHOICE_UPDATED_V2); + } + if !capabilities.new_payload_v2 { + missing_methods.push(ENGINE_NEW_PAYLOAD_V2); + } } - readiness => warn!( - hint = "try updating the execution endpoint", - info = %readiness, - "Not ready for Electra" - ), - } -} - -/// Provides some helpful logging to users to indicate if their node is ready for Fulu. -async fn fulu_readiness_logging( - current_slot: Slot, - beacon_chain: &BeaconChain, -) { - let fulu_completed = beacon_chain - .canonical_head - .cached_head() - .snapshot - .beacon_state - .fork_name_unchecked() - .fulu_enabled(); - - let has_execution_layer = beacon_chain.execution_layer.is_some(); - - if fulu_completed && has_execution_layer - || !beacon_chain.is_time_to_prepare_for_fulu(current_slot) - { - return; - } - - if fulu_completed && !has_execution_layer { - error!( - info = "you need a Fulu enabled execution engine to validate blocks.", - "Execution endpoint required" - ); - return; - } - - match beacon_chain.check_fulu_readiness().await { - FuluReadiness::Ready => { - info!( - info = "ensure the execution endpoint is updated to the latest Fulu release", - "Ready for Fulu" - ) + ForkName::Deneb => { + if !capabilities.get_payload_v3 { + missing_methods.push(ENGINE_GET_PAYLOAD_V3); + } + if !capabilities.forkchoice_updated_v3 { + missing_methods.push(ENGINE_FORKCHOICE_UPDATED_V3); + } + if !capabilities.new_payload_v3 { + missing_methods.push(ENGINE_NEW_PAYLOAD_V3); + } } - readiness @ FuluReadiness::ExchangeCapabilitiesFailed { error: _ } => { - error!( - hint = "the execution endpoint may be offline", - info = %readiness, - "Not ready for Fulu" - ) + ForkName::Electra => { + if !capabilities.get_payload_v4 { + missing_methods.push(ENGINE_GET_PAYLOAD_V4); + } + if !capabilities.new_payload_v4 { + missing_methods.push(ENGINE_NEW_PAYLOAD_V4); + } + } + ForkName::Fulu => { + // TODO(fulu) switch to v5 when the EL is ready + if !capabilities.get_payload_v4 { + missing_methods.push(ENGINE_GET_PAYLOAD_V4); + } + if !capabilities.new_payload_v4 { + missing_methods.push(ENGINE_NEW_PAYLOAD_V4); + } + } + ForkName::Gloas => { + // TODO(gloas) switch to v5/v6 when the EL is ready + if !capabilities.get_payload_v4 { + missing_methods.push(ENGINE_GET_PAYLOAD_V4); + } + if !capabilities.new_payload_v4 { + missing_methods.push(ENGINE_NEW_PAYLOAD_V4); + } } - readiness => warn!( - hint = "try updating the execution endpoint", - info = %readiness, - "Not ready for Fulu" - ), } + missing_methods } async fn genesis_execution_payload_logging(beacon_chain: &BeaconChain) { diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index e826bbb808a..98da7dbf2c7 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -25,7 +25,8 @@ pub use types::{ }; use types::{ ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, - ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionRequests, KzgProofs, + ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionRequests, + KzgProofs, }; use types::{GRAFFITI_BYTES_LEN, Graffiti}; @@ -37,6 +38,7 @@ mod new_payload_request; pub use new_payload_request::{ NewPayloadRequest, NewPayloadRequestBellatrix, NewPayloadRequestCapella, NewPayloadRequestDeneb, NewPayloadRequestElectra, NewPayloadRequestFulu, + NewPayloadRequestGloas, }; pub const LATEST_TAG: &str = "latest"; @@ -269,7 +271,7 @@ pub struct ProposeBlindedBlockResponse { } #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes(derive(Clone, Debug, PartialEq),), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), @@ -291,12 +293,14 @@ pub struct GetPayloadResponse { pub execution_payload: ExecutionPayloadElectra, #[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))] pub execution_payload: ExecutionPayloadFulu, + #[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))] + pub execution_payload: ExecutionPayloadGloas, pub block_value: Uint256, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] pub blobs_bundle: BlobsBundle, - #[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas), partial_getter(copy))] pub should_override_builder: bool, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub requests: ExecutionRequests, } @@ -370,6 +374,12 @@ impl From> Some(inner.blobs_bundle), Some(inner.requests), ), + GetPayloadResponse::Gloas(inner) => ( + ExecutionPayload::Gloas(inner.execution_payload), + inner.block_value, + Some(inner.blobs_bundle), + Some(inner.requests), + ), } } } @@ -531,6 +541,34 @@ impl ExecutionPayloadBodyV1 { )) } } + ExecutionPayloadHeader::Gloas(header) => { + if let Some(withdrawals) = self.withdrawals { + Ok(ExecutionPayload::Gloas(ExecutionPayloadGloas { + parent_hash: header.parent_hash, + fee_recipient: header.fee_recipient, + state_root: header.state_root, + receipts_root: header.receipts_root, + logs_bloom: header.logs_bloom, + prev_randao: header.prev_randao, + block_number: header.block_number, + gas_limit: header.gas_limit, + gas_used: header.gas_used, + timestamp: header.timestamp, + extra_data: header.extra_data, + base_fee_per_gas: header.base_fee_per_gas, + block_hash: header.block_hash, + transactions: self.transactions, + withdrawals, + blob_gas_used: header.blob_gas_used, + excess_blob_gas: header.excess_blob_gas, + })) + } else { + Err(format!( + "block {} is post capella but payload body doesn't have withdrawals", + header.block_hash + )) + } + } } } } diff --git a/beacon_node/execution_layer/src/engine_api/http.rs b/beacon_node/execution_layer/src/engine_api/http.rs index ad87dc6e03f..bc927e19b41 100644 --- a/beacon_node/execution_layer/src/engine_api/http.rs +++ b/beacon_node/execution_layer/src/engine_api/http.rs @@ -869,6 +869,30 @@ impl HttpJsonRpc { Ok(response.into()) } + pub async fn new_payload_v4_gloas( + &self, + new_payload_request_gloas: NewPayloadRequestGloas<'_, E>, + ) -> Result { + let params = json!([ + JsonExecutionPayload::Gloas(new_payload_request_gloas.execution_payload.clone().into()), + new_payload_request_gloas.versioned_hashes, + new_payload_request_gloas.parent_beacon_block_root, + new_payload_request_gloas + .execution_requests + .get_execution_requests_list(), + ]); + + let response: JsonPayloadStatusV1 = self + .rpc_request( + ENGINE_NEW_PAYLOAD_V4, + params, + ENGINE_NEW_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + + Ok(response.into()) + } + pub async fn get_payload_v1( &self, payload_id: PayloadId, @@ -1005,6 +1029,18 @@ impl HttpJsonRpc { .try_into() .map_err(Error::BadResponse) } + ForkName::Gloas => { + let response: JsonGetPayloadResponseGloas = self + .rpc_request( + ENGINE_GET_PAYLOAD_V5, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + JsonGetPayloadResponse::Gloas(response) + .try_into() + .map_err(Error::BadResponse) + } _ => Err(Error::UnsupportedForkVariant(format!( "called get_payload_v5 with {}", fork_name @@ -1289,6 +1325,13 @@ impl HttpJsonRpc { Err(Error::RequiredMethodUnsupported("engine_newPayloadV4")) } } + NewPayloadRequest::Gloas(new_payload_request_gloas) => { + if engine_capabilities.new_payload_v4 { + self.new_payload_v4_gloas(new_payload_request_gloas).await + } else { + Err(Error::RequiredMethodUnsupported("engine_newPayloadV4")) + } + } } } @@ -1331,6 +1374,13 @@ impl HttpJsonRpc { Err(Error::RequiredMethodUnsupported("engine_getPayloadv5")) } } + ForkName::Gloas => { + if engine_capabilities.get_payload_v5 { + self.get_payload_v5(fork_name, payload_id).await + } else { + Err(Error::RequiredMethodUnsupported("engine_getPayloadv5")) + } + } ForkName::Base | ForkName::Altair => Err(Error::UnsupportedForkVariant(format!( "called get_payload with {}", fork_name diff --git a/beacon_node/execution_layer/src/engine_api/json_structures.rs b/beacon_node/execution_layer/src/engine_api/json_structures.rs index 5db5e2c9f6d..33decd4ec86 100644 --- a/beacon_node/execution_layer/src/engine_api/json_structures.rs +++ b/beacon_node/execution_layer/src/engine_api/json_structures.rs @@ -65,7 +65,7 @@ pub struct JsonPayloadIdResponse { } #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive(Debug, PartialEq, Default, Serialize, Deserialize,), serde(bound = "E: EthSpec", rename_all = "camelCase"), @@ -100,12 +100,12 @@ pub struct JsonExecutionPayload { pub block_hash: ExecutionBlockHash, #[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")] pub transactions: Transactions, - #[superstruct(only(Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))] pub withdrawals: VariableList, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] #[serde(with = "serde_utils::u64_hex_be")] pub blob_gas_used: u64, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] #[serde(with = "serde_utils::u64_hex_be")] pub excess_blob_gas: u64, } @@ -243,6 +243,35 @@ impl From> for JsonExecutionPayloadFulu { } } +impl From> for JsonExecutionPayloadGloas { + fn from(payload: ExecutionPayloadGloas) -> Self { + JsonExecutionPayloadGloas { + parent_hash: payload.parent_hash, + fee_recipient: payload.fee_recipient, + state_root: payload.state_root, + receipts_root: payload.receipts_root, + logs_bloom: payload.logs_bloom, + prev_randao: payload.prev_randao, + block_number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + extra_data: payload.extra_data, + base_fee_per_gas: payload.base_fee_per_gas, + block_hash: payload.block_hash, + transactions: payload.transactions, + withdrawals: payload + .withdrawals + .into_iter() + .map(Into::into) + .collect::>() + .into(), + blob_gas_used: payload.blob_gas_used, + excess_blob_gas: payload.excess_blob_gas, + } + } +} + impl From> for JsonExecutionPayload { fn from(execution_payload: ExecutionPayload) -> Self { match execution_payload { @@ -251,6 +280,7 @@ impl From> for JsonExecutionPayload { ExecutionPayload::Deneb(payload) => JsonExecutionPayload::Deneb(payload.into()), ExecutionPayload::Electra(payload) => JsonExecutionPayload::Electra(payload.into()), ExecutionPayload::Fulu(payload) => JsonExecutionPayload::Fulu(payload.into()), + ExecutionPayload::Gloas(payload) => JsonExecutionPayload::Gloas(payload.into()), } } } @@ -389,6 +419,35 @@ impl From> for ExecutionPayloadFulu { } } +impl From> for ExecutionPayloadGloas { + fn from(payload: JsonExecutionPayloadGloas) -> Self { + ExecutionPayloadGloas { + parent_hash: payload.parent_hash, + fee_recipient: payload.fee_recipient, + state_root: payload.state_root, + receipts_root: payload.receipts_root, + logs_bloom: payload.logs_bloom, + prev_randao: payload.prev_randao, + block_number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + extra_data: payload.extra_data, + base_fee_per_gas: payload.base_fee_per_gas, + block_hash: payload.block_hash, + transactions: payload.transactions, + withdrawals: payload + .withdrawals + .into_iter() + .map(Into::into) + .collect::>() + .into(), + blob_gas_used: payload.blob_gas_used, + excess_blob_gas: payload.excess_blob_gas, + } + } +} + impl From> for ExecutionPayload { fn from(json_execution_payload: JsonExecutionPayload) -> Self { match json_execution_payload { @@ -397,6 +456,7 @@ impl From> for ExecutionPayload { JsonExecutionPayload::Deneb(payload) => ExecutionPayload::Deneb(payload.into()), JsonExecutionPayload::Electra(payload) => ExecutionPayload::Electra(payload.into()), JsonExecutionPayload::Fulu(payload) => ExecutionPayload::Fulu(payload.into()), + JsonExecutionPayload::Gloas(payload) => ExecutionPayload::Gloas(payload.into()), } } } @@ -482,7 +542,7 @@ impl TryFrom for ExecutionRequests { } #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive(Debug, PartialEq, Serialize, Deserialize), serde(bound = "E: EthSpec", rename_all = "camelCase") @@ -506,13 +566,15 @@ pub struct JsonGetPayloadResponse { pub execution_payload: JsonExecutionPayloadElectra, #[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))] pub execution_payload: JsonExecutionPayloadFulu, + #[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))] + pub execution_payload: JsonExecutionPayloadGloas, #[serde(with = "serde_utils::u256_hex_be")] pub block_value: Uint256, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] pub blobs_bundle: JsonBlobsBundleV1, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] pub should_override_builder: bool, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub execution_requests: JsonExecutionRequests, } @@ -562,6 +624,17 @@ impl TryFrom> for GetPayloadResponse { })?, })) } + JsonGetPayloadResponse::Gloas(response) => { + Ok(GetPayloadResponse::Gloas(GetPayloadResponseGloas { + execution_payload: response.execution_payload.into(), + block_value: response.block_value, + blobs_bundle: response.blobs_bundle.into(), + should_override_builder: response.should_override_builder, + requests: response.execution_requests.try_into().map_err(|e| { + format!("Failed to convert json to execution requests {:?}", e) + })?, + })) + } } } } diff --git a/beacon_node/execution_layer/src/engine_api/new_payload_request.rs b/beacon_node/execution_layer/src/engine_api/new_payload_request.rs index 85bec9e9ac2..aa5261c80b0 100644 --- a/beacon_node/execution_layer/src/engine_api/new_payload_request.rs +++ b/beacon_node/execution_layer/src/engine_api/new_payload_request.rs @@ -9,11 +9,11 @@ use types::{ }; use types::{ ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, - ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionRequests, + ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionRequests, }; #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes(derive(Clone, Debug, PartialEq),), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), @@ -41,11 +41,13 @@ pub struct NewPayloadRequest<'block, E: EthSpec> { pub execution_payload: &'block ExecutionPayloadElectra, #[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))] pub execution_payload: &'block ExecutionPayloadFulu, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))] + pub execution_payload: &'block ExecutionPayloadGloas, + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] pub versioned_hashes: Vec, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] pub parent_beacon_block_root: Hash256, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub execution_requests: &'block ExecutionRequests, } @@ -57,6 +59,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { Self::Deneb(payload) => payload.execution_payload.parent_hash, Self::Electra(payload) => payload.execution_payload.parent_hash, Self::Fulu(payload) => payload.execution_payload.parent_hash, + Self::Gloas(payload) => payload.execution_payload.parent_hash, } } @@ -67,6 +70,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { Self::Deneb(payload) => payload.execution_payload.block_hash, Self::Electra(payload) => payload.execution_payload.block_hash, Self::Fulu(payload) => payload.execution_payload.block_hash, + Self::Gloas(payload) => payload.execution_payload.block_hash, } } @@ -77,6 +81,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { Self::Deneb(payload) => payload.execution_payload.block_number, Self::Electra(payload) => payload.execution_payload.block_number, Self::Fulu(payload) => payload.execution_payload.block_number, + Self::Gloas(payload) => payload.execution_payload.block_number, } } @@ -87,6 +92,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { Self::Deneb(request) => ExecutionPayloadRef::Deneb(request.execution_payload), Self::Electra(request) => ExecutionPayloadRef::Electra(request.execution_payload), Self::Fulu(request) => ExecutionPayloadRef::Fulu(request.execution_payload), + Self::Gloas(request) => ExecutionPayloadRef::Gloas(request.execution_payload), } } @@ -99,6 +105,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { Self::Deneb(request) => ExecutionPayload::Deneb(request.execution_payload.clone()), Self::Electra(request) => ExecutionPayload::Electra(request.execution_payload.clone()), Self::Fulu(request) => ExecutionPayload::Fulu(request.execution_payload.clone()), + Self::Gloas(request) => ExecutionPayload::Gloas(request.execution_payload.clone()), } } @@ -213,6 +220,17 @@ impl<'a, E: EthSpec> TryFrom> for NewPayloadRequest<'a, E> parent_beacon_block_root: block_ref.parent_root, execution_requests: &block_ref.body.execution_requests, })), + BeaconBlockRef::Gloas(block_ref) => Ok(Self::Gloas(NewPayloadRequestGloas { + execution_payload: &block_ref.body.execution_payload.execution_payload, + versioned_hashes: block_ref + .body + .blob_kzg_commitments + .iter() + .map(kzg_commitment_to_versioned_hash) + .collect(), + parent_beacon_block_root: block_ref.parent_root, + execution_requests: &block_ref.body.execution_requests, + })), } } } @@ -233,6 +251,7 @@ impl<'a, E: EthSpec> TryFrom> for NewPayloadRequest<' ExecutionPayloadRef::Deneb(_) => Err(Self::Error::IncorrectStateVariant), ExecutionPayloadRef::Electra(_) => Err(Self::Error::IncorrectStateVariant), ExecutionPayloadRef::Fulu(_) => Err(Self::Error::IncorrectStateVariant), + ExecutionPayloadRef::Gloas(_) => Err(Self::Error::IncorrectStateVariant), } } } diff --git a/beacon_node/execution_layer/src/lib.rs b/beacon_node/execution_layer/src/lib.rs index 69fc342368a..b53c4cde4e6 100644 --- a/beacon_node/execution_layer/src/lib.rs +++ b/beacon_node/execution_layer/src/lib.rs @@ -55,8 +55,8 @@ use types::{ }; use types::{ BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadBellatrix, - ExecutionPayloadCapella, ExecutionPayloadElectra, ExecutionPayloadFulu, FullPayload, - ProposerPreparationData, PublicKeyBytes, Signature, Slot, + ExecutionPayloadCapella, ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, + FullPayload, ProposerPreparationData, PublicKeyBytes, Signature, Slot, }; mod block_hash; @@ -131,6 +131,13 @@ impl TryFrom> for ProvenancedPayload BlockProposalContents::PayloadAndBlobs { + payload: ExecutionPayloadHeader::Gloas(builder_bid.header).into(), + block_value: builder_bid.value, + kzg_commitments: builder_bid.blob_kzg_commitments, + blobs_and_proofs: None, + requests: Some(builder_bid.execution_requests), + }, }; Ok(ProvenancedPayload::Builder( BlockProposalContentsType::Blinded(block_proposal_contents), @@ -1820,6 +1827,7 @@ impl ExecutionLayer { ForkName::Deneb => ExecutionPayloadDeneb::default().into(), ForkName::Electra => ExecutionPayloadElectra::default().into(), ForkName::Fulu => ExecutionPayloadFulu::default().into(), + ForkName::Gloas => ExecutionPayloadGloas::default().into(), ForkName::Base | ForkName::Altair => { return Err(Error::InvalidForkForPayload); } diff --git a/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs b/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs index 028c185de5d..4836f9307c8 100644 --- a/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs +++ b/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs @@ -20,8 +20,8 @@ use tree_hash_derive::TreeHash; use types::{ Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu, - ExecutionPayloadHeader, FixedBytesExtended, ForkName, Hash256, KzgProofs, Transaction, - Transactions, Uint256, + ExecutionPayloadGloas, ExecutionPayloadHeader, FixedBytesExtended, ForkName, Hash256, + KzgProofs, Transaction, Transactions, Uint256, }; use super::DEFAULT_TERMINAL_BLOCK; @@ -146,10 +146,11 @@ pub struct ExecutionBlockGenerator { /* * Post-merge fork triggers */ - pub shanghai_time: Option, // capella - pub cancun_time: Option, // deneb - pub prague_time: Option, // electra - pub osaka_time: Option, // fulu + pub shanghai_time: Option, // capella + pub cancun_time: Option, // deneb + pub prague_time: Option, // electra + pub osaka_time: Option, // fulu + pub amsterdam_time: Option, // gloas /* * deneb stuff */ @@ -175,6 +176,7 @@ impl ExecutionBlockGenerator { cancun_time: Option, prague_time: Option, osaka_time: Option, + amsterdam_time: Option, spec: Arc, kzg: Option>, ) -> Self { @@ -194,6 +196,7 @@ impl ExecutionBlockGenerator { cancun_time, prague_time, osaka_time, + amsterdam_time, blobs_bundles: <_>::default(), kzg, rng: make_rng(), @@ -243,19 +246,23 @@ impl ExecutionBlockGenerator { } pub fn get_fork_at_timestamp(&self, timestamp: u64) -> ForkName { - match self.osaka_time { - Some(fork_time) if timestamp >= fork_time => ForkName::Fulu, - _ => match self.prague_time { - Some(fork_time) if timestamp >= fork_time => ForkName::Electra, - _ => match self.cancun_time { - Some(fork_time) if timestamp >= fork_time => ForkName::Deneb, - _ => match self.shanghai_time { - Some(fork_time) if timestamp >= fork_time => ForkName::Capella, - _ => ForkName::Bellatrix, - }, - }, - }, + let forks = [ + (self.amsterdam_time, ForkName::Gloas), + (self.osaka_time, ForkName::Fulu), + (self.prague_time, ForkName::Electra), + (self.cancun_time, ForkName::Deneb), + (self.shanghai_time, ForkName::Capella), + ]; + + for (fork_time, fork_name) in forks { + if let Some(time) = fork_time + && timestamp >= time + { + return fork_name; + } } + + ForkName::Bellatrix } pub fn execution_block_by_number(&self, number: u64) -> Option { @@ -700,6 +707,25 @@ impl ExecutionBlockGenerator { blob_gas_used: 0, excess_blob_gas: 0, }), + ForkName::Gloas => ExecutionPayload::Gloas(ExecutionPayloadGloas { + parent_hash: head_block_hash, + fee_recipient: pa.suggested_fee_recipient, + receipts_root: Hash256::repeat_byte(42), + state_root: Hash256::repeat_byte(43), + logs_bloom: vec![0; 256].into(), + prev_randao: pa.prev_randao, + block_number: parent.block_number() + 1, + gas_limit: DEFAULT_GAS_LIMIT, + gas_used: GAS_USED, + timestamp: pa.timestamp, + extra_data: "block gen was here".as_bytes().to_vec().into(), + base_fee_per_gas: Uint256::from(1u64), + block_hash: ExecutionBlockHash::zero(), + transactions: vec![].into(), + withdrawals: pa.withdrawals.clone().into(), + blob_gas_used: 0, + excess_blob_gas: 0, + }), _ => unreachable!(), }, }; @@ -882,6 +908,12 @@ pub fn generate_genesis_header( *header.transactions_root_mut() = empty_transactions_root; Some(header) } + ForkName::Gloas => { + let mut header = ExecutionPayloadHeader::Gloas(<_>::default()); + *header.block_hash_mut() = genesis_block_hash.unwrap_or_default(); + *header.transactions_root_mut() = empty_transactions_root; + Some(header) + } } } @@ -955,6 +987,7 @@ mod test { None, None, None, + None, spec, None, ); diff --git a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs index ce42fc0897f..7a451beddb3 100644 --- a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs +++ b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs @@ -115,8 +115,12 @@ pub async fn handle_rpc( ENGINE_NEW_PAYLOAD_V3 => get_param::>(params, 0) .map(|jep| JsonExecutionPayload::Deneb(jep)) .map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?, - ENGINE_NEW_PAYLOAD_V4 => get_param::>(params, 0) - .map(|jep| JsonExecutionPayload::Fulu(jep)) + ENGINE_NEW_PAYLOAD_V4 => get_param::>(params, 0) + .map(|jep| JsonExecutionPayload::Gloas(jep)) + .or_else(|_| { + get_param::>(params, 0) + .map(|jep| JsonExecutionPayload::Fulu(jep)) + }) .or_else(|_| { get_param::>(params, 0) .map(|jep| JsonExecutionPayload::Electra(jep)) @@ -185,7 +189,7 @@ pub async fn handle_rpc( )); } } - ForkName::Electra | ForkName::Fulu => { + ForkName::Electra | ForkName::Fulu | ForkName::Gloas => { if method == ENGINE_NEW_PAYLOAD_V1 || method == ENGINE_NEW_PAYLOAD_V2 || method == ENGINE_NEW_PAYLOAD_V3 @@ -322,7 +326,7 @@ pub async fn handle_rpc( )); } - // validate method called correctly according to fulu fork time + // validate method called correctly according to osaka fork time if ctx .execution_block_generator .read() @@ -339,6 +343,23 @@ pub async fn handle_rpc( )); } + // validate method called correctly according to amsterdam fork time + if ctx + .execution_block_generator + .read() + .get_fork_at_timestamp(response.timestamp()) + == ForkName::Gloas + && (method == ENGINE_GET_PAYLOAD_V1 + || method == ENGINE_GET_PAYLOAD_V2 + || method == ENGINE_GET_PAYLOAD_V3 + || method == ENGINE_GET_PAYLOAD_V4) + { + return Err(( + format!("{} called after Gloas fork!", method), + FORK_REQUEST_MISMATCH_ERROR_CODE, + )); + } + match method { ENGINE_GET_PAYLOAD_V1 => { Ok(serde_json::to_value(JsonExecutionPayload::from(response)).unwrap()) @@ -415,6 +436,21 @@ pub async fn handle_rpc( }) .unwrap() } + JsonExecutionPayload::Gloas(execution_payload) => { + serde_json::to_value(JsonGetPayloadResponseGloas { + execution_payload, + block_value: Uint256::from(DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI), + blobs_bundle: maybe_blobs + .ok_or(( + "No blobs returned despite V5 Payload".to_string(), + GENERIC_ERROR_CODE, + ))? + .into(), + should_override_builder: false, + execution_requests: Default::default(), + }) + .unwrap() + } _ => unreachable!(), }), _ => unreachable!(), @@ -451,7 +487,8 @@ pub async fn handle_rpc( ForkName::Capella | ForkName::Deneb | ForkName::Electra - | ForkName::Fulu => { + | ForkName::Fulu + | ForkName::Gloas => { get_param::>(params, 1) .map(|opt| opt.map(JsonPayloadAttributes::V2)) .transpose() @@ -515,7 +552,7 @@ pub async fn handle_rpc( )); } } - ForkName::Deneb | ForkName::Electra | ForkName::Fulu => { + ForkName::Deneb | ForkName::Electra | ForkName::Fulu | ForkName::Gloas => { if method == ENGINE_FORKCHOICE_UPDATED_V1 { return Err(( format!("{} called after Deneb fork!", method), diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 38fbc73fb41..516662b1d64 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -27,7 +27,7 @@ use tracing::{debug, error, info, warn}; use tree_hash::TreeHash; use types::builder_bid::{ BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra, - BuilderBidFulu, SignedBuilderBid, + BuilderBidFulu, BuilderBidGloas, SignedBuilderBid, }; use types::{ Address, BeaconState, ChainSpec, Epoch, EthSpec, ExecPayload, ExecutionPayload, @@ -115,6 +115,9 @@ impl BidStuff for BuilderBid { ExecutionPayloadHeaderRefMut::Fulu(header) => { header.fee_recipient = fee_recipient; } + ExecutionPayloadHeaderRefMut::Gloas(header) => { + header.fee_recipient = fee_recipient; + } } } @@ -135,6 +138,9 @@ impl BidStuff for BuilderBid { ExecutionPayloadHeaderRefMut::Fulu(header) => { header.gas_limit = gas_limit; } + ExecutionPayloadHeaderRefMut::Gloas(header) => { + header.gas_limit = gas_limit; + } } } @@ -159,6 +165,9 @@ impl BidStuff for BuilderBid { ExecutionPayloadHeaderRefMut::Fulu(header) => { header.parent_hash = ExecutionBlockHash::from_root(parent_hash); } + ExecutionPayloadHeaderRefMut::Gloas(header) => { + header.parent_hash = ExecutionBlockHash::from_root(parent_hash); + } } } @@ -179,6 +188,9 @@ impl BidStuff for BuilderBid { ExecutionPayloadHeaderRefMut::Fulu(header) => { header.prev_randao = prev_randao; } + ExecutionPayloadHeaderRefMut::Gloas(header) => { + header.prev_randao = prev_randao; + } } } @@ -199,6 +211,9 @@ impl BidStuff for BuilderBid { ExecutionPayloadHeaderRefMut::Fulu(header) => { header.block_number = block_number; } + ExecutionPayloadHeaderRefMut::Gloas(header) => { + header.block_number = block_number; + } } } @@ -219,6 +234,9 @@ impl BidStuff for BuilderBid { ExecutionPayloadHeaderRefMut::Fulu(header) => { header.timestamp = timestamp; } + ExecutionPayloadHeaderRefMut::Gloas(header) => { + header.timestamp = timestamp; + } } } @@ -239,6 +257,9 @@ impl BidStuff for BuilderBid { ExecutionPayloadHeaderRefMut::Fulu(header) => { header.withdrawals_root = withdrawals_root; } + ExecutionPayloadHeaderRefMut::Gloas(header) => { + header.withdrawals_root = withdrawals_root; + } } } @@ -272,6 +293,10 @@ impl BidStuff for BuilderBid { header.extra_data = extra_data; header.block_hash = ExecutionBlockHash::from_root(header.tree_hash_root()); } + ExecutionPayloadHeaderRefMut::Gloas(header) => { + header.extra_data = extra_data; + header.block_hash = ExecutionBlockHash::from_root(header.tree_hash_root()); + } } } } @@ -457,6 +482,9 @@ impl MockBuilder { SignedBlindedBeaconBlock::Fulu(block) => { block.message.body.execution_payload.tree_hash_root() } + SignedBlindedBeaconBlock::Gloas(block) => { + block.message.body.execution_payload.tree_hash_root() + } }; info!( block_hash = %root, @@ -540,6 +568,18 @@ impl MockBuilder { ) = payload_response.into(); match fork { + ForkName::Gloas => BuilderBid::Gloas(BuilderBidGloas { + header: payload + .as_gloas() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + blob_kzg_commitments: maybe_blobs_bundle + .map(|b| b.commitments.clone()) + .unwrap_or_default(), + value: self.get_bid_value(value), + pubkey: self.builder_sk.public_key().compress(), + execution_requests: maybe_requests.unwrap_or_default(), + }), ForkName::Fulu => BuilderBid::Fulu(BuilderBidFulu { header: payload .as_fulu() @@ -845,13 +885,15 @@ impl MockBuilder { expected_withdrawals, None, ), - ForkName::Deneb | ForkName::Electra | ForkName::Fulu => PayloadAttributes::new( - timestamp, - *prev_randao, - fee_recipient, - expected_withdrawals, - Some(head_block_root), - ), + ForkName::Deneb | ForkName::Electra | ForkName::Fulu | ForkName::Gloas => { + PayloadAttributes::new( + timestamp, + *prev_randao, + fee_recipient, + expected_withdrawals, + Some(head_block_root), + ) + } ForkName::Base | ForkName::Altair => { return Err("invalid fork".to_string()); } diff --git a/beacon_node/execution_layer/src/test_utils/mock_execution_layer.rs b/beacon_node/execution_layer/src/test_utils/mock_execution_layer.rs index 4b56c771294..9e587d4e590 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_execution_layer.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_execution_layer.rs @@ -29,6 +29,7 @@ impl MockExecutionLayer { None, None, None, + None, Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()), Arc::new(spec), None, @@ -43,6 +44,7 @@ impl MockExecutionLayer { cancun_time: Option, prague_time: Option, osaka_time: Option, + amsterdam_time: Option, jwt_key: Option, spec: Arc, kzg: Option>, @@ -60,6 +62,7 @@ impl MockExecutionLayer { cancun_time, prague_time, osaka_time, + amsterdam_time, spec.clone(), kzg, ); diff --git a/beacon_node/execution_layer/src/test_utils/mod.rs b/beacon_node/execution_layer/src/test_utils/mod.rs index 9253c003399..712c773dda0 100644 --- a/beacon_node/execution_layer/src/test_utils/mod.rs +++ b/beacon_node/execution_layer/src/test_utils/mod.rs @@ -86,6 +86,7 @@ pub struct MockExecutionConfig { pub cancun_time: Option, pub prague_time: Option, pub osaka_time: Option, + pub amsterdam_time: Option, } impl Default for MockExecutionConfig { @@ -100,6 +101,7 @@ impl Default for MockExecutionConfig { cancun_time: None, prague_time: None, osaka_time: None, + amsterdam_time: None, } } } @@ -123,6 +125,7 @@ impl MockServer { None, // FIXME(deneb): should this be the default? None, // FIXME(electra): should this be the default? None, // FIXME(fulu): should this be the default? + None, // FIXME(gloas): should this be the default? chain_spec, None, ) @@ -145,6 +148,7 @@ impl MockServer { cancun_time, prague_time, osaka_time, + amsterdam_time, } = config; let last_echo_request = Arc::new(RwLock::new(None)); let preloaded_responses = Arc::new(Mutex::new(vec![])); @@ -156,6 +160,7 @@ impl MockServer { cancun_time, prague_time, osaka_time, + amsterdam_time, spec, kzg, ); @@ -220,6 +225,7 @@ impl MockServer { cancun_time: Option, prague_time: Option, osaka_time: Option, + amsterdam_time: Option, spec: Arc, kzg: Option>, ) -> Self { @@ -235,6 +241,7 @@ impl MockServer { cancun_time, prague_time, osaka_time, + amsterdam_time, }, spec, kzg, diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 69d2b5c21b3..92abbd84c76 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -2651,7 +2651,12 @@ impl ApiTester { } pub async fn test_get_config_spec(self) -> Self { - let result = if self.chain.spec.is_fulu_scheduled() { + let result = if self.chain.spec.is_gloas_scheduled() { + self.client + .get_config_spec::() + .await + .map(|res| ConfigAndPreset::Gloas(res.data)) + } else if self.chain.spec.is_fulu_scheduled() { self.client .get_config_spec::() .await diff --git a/beacon_node/lighthouse_network/src/rpc/codec.rs b/beacon_node/lighthouse_network/src/rpc/codec.rs index e10b8c4ecc2..91f9960d47e 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec.rs @@ -21,7 +21,7 @@ use types::{ LightClientOptimisticUpdate, LightClientUpdate, RuntimeVariableList, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix, SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, - SignedBeaconBlockFulu, + SignedBeaconBlockFulu, SignedBeaconBlockGloas, }; use unsigned_varint::codec::Uvi; @@ -830,6 +830,9 @@ fn handle_rpc_response( Some(ForkName::Fulu) => Ok(Some(RpcSuccessResponse::BlocksByRange(Arc::new( SignedBeaconBlock::Fulu(SignedBeaconBlockFulu::from_ssz_bytes(decoded_buffer)?), )))), + Some(ForkName::Gloas) => Ok(Some(RpcSuccessResponse::BlocksByRange(Arc::new( + SignedBeaconBlock::Gloas(SignedBeaconBlockGloas::from_ssz_bytes(decoded_buffer)?), + )))), None => Err(RPCError::ErrorResponse( RpcErrorResponse::InvalidRequest, format!( @@ -866,6 +869,9 @@ fn handle_rpc_response( Some(ForkName::Fulu) => Ok(Some(RpcSuccessResponse::BlocksByRoot(Arc::new( SignedBeaconBlock::Fulu(SignedBeaconBlockFulu::from_ssz_bytes(decoded_buffer)?), )))), + Some(ForkName::Gloas) => Ok(Some(RpcSuccessResponse::BlocksByRoot(Arc::new( + SignedBeaconBlock::Gloas(SignedBeaconBlockGloas::from_ssz_bytes(decoded_buffer)?), + )))), None => Err(RPCError::ErrorResponse( RpcErrorResponse::InvalidRequest, format!( @@ -919,6 +925,7 @@ mod tests { chain_spec.deneb_fork_epoch = Some(Epoch::new(4)); chain_spec.electra_fork_epoch = Some(Epoch::new(5)); chain_spec.fulu_fork_epoch = Some(Epoch::new(6)); + chain_spec.gloas_fork_epoch = Some(Epoch::new(7)); // check that we have all forks covered assert!(chain_spec.fork_epoch(ForkName::latest()).is_some()); @@ -934,6 +941,7 @@ mod tests { ForkName::Deneb => spec.deneb_fork_epoch, ForkName::Electra => spec.electra_fork_epoch, ForkName::Fulu => spec.fulu_fork_epoch, + ForkName::Gloas => spec.gloas_fork_epoch, }; let current_slot = current_epoch.unwrap().start_slot(Spec::slots_per_epoch()); ForkContext::new::(current_slot, Hash256::zero(), spec) @@ -1294,7 +1302,7 @@ mod tests { encode_then_decode_response( SupportedProtocol::StatusV1, RpcResponse::Success(RpcSuccessResponse::Status(status_message_v2())), - ForkName::Fulu, + ForkName::Gloas, &chain_spec, ), Ok(Some(RpcSuccessResponse::Status(status_message_v1()))) diff --git a/beacon_node/lighthouse_network/src/rpc/protocol.rs b/beacon_node/lighthouse_network/src/rpc/protocol.rs index a8f0be87a39..6529ff5f927 100644 --- a/beacon_node/lighthouse_network/src/rpc/protocol.rs +++ b/beacon_node/lighthouse_network/src/rpc/protocol.rs @@ -158,7 +158,7 @@ fn rpc_light_client_updates_by_range_limits_by_fork(current_fork: ForkName) -> R ForkName::Deneb => { RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_UPDATES_BY_RANGE_DENEB_MAX) } - ForkName::Electra | ForkName::Fulu => { + ForkName::Electra | ForkName::Fulu | ForkName::Gloas => { RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_UPDATES_BY_RANGE_ELECTRA_MAX) } } @@ -178,7 +178,7 @@ fn rpc_light_client_finality_update_limits_by_fork(current_fork: ForkName) -> Rp ForkName::Deneb => { RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_FINALITY_UPDATE_DENEB_MAX) } - ForkName::Electra | ForkName::Fulu => { + ForkName::Electra | ForkName::Fulu | ForkName::Gloas => { RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_FINALITY_UPDATE_ELECTRA_MAX) } } @@ -199,7 +199,7 @@ fn rpc_light_client_optimistic_update_limits_by_fork(current_fork: ForkName) -> ForkName::Deneb => { RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_OPTIMISTIC_UPDATE_DENEB_MAX) } - ForkName::Electra | ForkName::Fulu => RpcLimits::new( + ForkName::Electra | ForkName::Fulu | ForkName::Gloas => RpcLimits::new( altair_fixed_len, *LIGHT_CLIENT_OPTIMISTIC_UPDATE_ELECTRA_MAX, ), @@ -216,7 +216,7 @@ fn rpc_light_client_bootstrap_limits_by_fork(current_fork: ForkName) -> RpcLimit } ForkName::Capella => RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_CAPELLA_MAX), ForkName::Deneb => RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_DENEB_MAX), - ForkName::Electra | ForkName::Fulu => { + ForkName::Electra | ForkName::Fulu | ForkName::Gloas => { RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_ELECTRA_MAX) } } diff --git a/beacon_node/lighthouse_network/src/types/pubsub.rs b/beacon_node/lighthouse_network/src/types/pubsub.rs index 567c76a02c0..72f2873def9 100644 --- a/beacon_node/lighthouse_network/src/types/pubsub.rs +++ b/beacon_node/lighthouse_network/src/types/pubsub.rs @@ -13,8 +13,9 @@ use types::{ SignedAggregateAndProof, SignedAggregateAndProofBase, SignedAggregateAndProofElectra, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix, SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, - SignedBeaconBlockFulu, SignedBlsToExecutionChange, SignedContributionAndProof, - SignedVoluntaryExit, SingleAttestation, SubnetId, SyncCommitteeMessage, SyncSubnetId, + SignedBeaconBlockFulu, SignedBeaconBlockGloas, SignedBlsToExecutionChange, + SignedContributionAndProof, SignedVoluntaryExit, SingleAttestation, SubnetId, + SyncCommitteeMessage, SyncSubnetId, }; #[derive(Debug, Clone, PartialEq)] @@ -238,6 +239,10 @@ impl PubsubMessage { SignedBeaconBlockFulu::from_ssz_bytes(data) .map_err(|e| format!("{:?}", e))?, ), + Some(ForkName::Gloas) => SignedBeaconBlock::::Gloas( + SignedBeaconBlockGloas::from_ssz_bytes(data) + .map_err(|e| format!("{:?}", e))?, + ), None => { return Err(format!( "Unknown gossipsub fork digest: {:?}", diff --git a/beacon_node/lighthouse_network/tests/common.rs b/beacon_node/lighthouse_network/tests/common.rs index 0d1a623fbdd..6b111cfdc1e 100644 --- a/beacon_node/lighthouse_network/tests/common.rs +++ b/beacon_node/lighthouse_network/tests/common.rs @@ -28,6 +28,7 @@ pub fn spec_with_all_forks_enabled() -> ChainSpec { chain_spec.deneb_fork_epoch = Some(Epoch::new(4)); chain_spec.electra_fork_epoch = Some(Epoch::new(5)); chain_spec.fulu_fork_epoch = Some(Epoch::new(6)); + chain_spec.gloas_fork_epoch = Some(Epoch::new(7)); // check that we have all forks covered assert!(chain_spec.fork_epoch(ForkName::latest()).is_some()); @@ -44,6 +45,7 @@ pub fn fork_context(fork_name: ForkName, spec: &ChainSpec) -> ForkContext { ForkName::Deneb => spec.deneb_fork_epoch, ForkName::Electra => spec.electra_fork_epoch, ForkName::Fulu => spec.fulu_fork_epoch, + ForkName::Gloas => spec.gloas_fork_epoch, }; let current_slot = current_epoch .unwrap_or_else(|| panic!("expect fork {fork_name} to be scheduled")) diff --git a/beacon_node/network/src/subnet_service/tests/mod.rs b/beacon_node/network/src/subnet_service/tests/mod.rs index 03627dde054..0df28cff6b7 100644 --- a/beacon_node/network/src/subnet_service/tests/mod.rs +++ b/beacon_node/network/src/subnet_service/tests/mod.rs @@ -94,10 +94,9 @@ pub fn recent_genesis_time() -> u64 { fn get_tracing_subscriber(log_level: Option<&str>) { if let Some(level) = log_level { - tracing_subscriber::fmt() + let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::try_new(level).unwrap()) - .try_init() - .unwrap(); + .try_init(); } } diff --git a/beacon_node/src/lib.rs b/beacon_node/src/lib.rs index 96abae735b2..2ceb94729d5 100644 --- a/beacon_node/src/lib.rs +++ b/beacon_node/src/lib.rs @@ -205,6 +205,7 @@ mod test { spec.deneb_fork_epoch = Some(Epoch::new(257)); spec.electra_fork_epoch = None; spec.fulu_fork_epoch = None; + spec.gloas_fork_epoch = None; let result = validator_fork_epochs(&spec); assert_eq!( result, diff --git a/beacon_node/store/src/impls/execution_payload.rs b/beacon_node/store/src/impls/execution_payload.rs index 097b069a665..1556dab1a4d 100644 --- a/beacon_node/store/src/impls/execution_payload.rs +++ b/beacon_node/store/src/impls/execution_payload.rs @@ -2,7 +2,7 @@ use crate::{DBColumn, Error, StoreItem}; use ssz::{Decode, Encode}; use types::{ EthSpec, ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, - ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu, + ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, }; macro_rules! impl_store_item { @@ -27,6 +27,7 @@ impl_store_item!(ExecutionPayloadCapella); impl_store_item!(ExecutionPayloadDeneb); impl_store_item!(ExecutionPayloadElectra); impl_store_item!(ExecutionPayloadFulu); +impl_store_item!(ExecutionPayloadGloas); /// This fork-agnostic implementation should be only used for writing. /// @@ -42,24 +43,28 @@ impl StoreItem for ExecutionPayload { } fn from_store_bytes(bytes: &[u8]) -> Result { - ExecutionPayloadFulu::from_ssz_bytes(bytes) - .map(Self::Fulu) - .or_else(|_| { - ExecutionPayloadElectra::from_ssz_bytes(bytes) - .map(Self::Electra) - .or_else(|_| { - ExecutionPayloadDeneb::from_ssz_bytes(bytes) - .map(Self::Deneb) - .or_else(|_| { - ExecutionPayloadCapella::from_ssz_bytes(bytes) - .map(Self::Capella) - .or_else(|_| { - ExecutionPayloadBellatrix::from_ssz_bytes(bytes) - .map(Self::Bellatrix) - }) - }) - }) - }) + if let Ok(payload) = ExecutionPayloadGloas::from_ssz_bytes(bytes) { + return Ok(Self::Gloas(payload)); + } + + if let Ok(payload) = ExecutionPayloadFulu::from_ssz_bytes(bytes) { + return Ok(Self::Fulu(payload)); + } + + if let Ok(payload) = ExecutionPayloadElectra::from_ssz_bytes(bytes) { + return Ok(Self::Electra(payload)); + } + + if let Ok(payload) = ExecutionPayloadDeneb::from_ssz_bytes(bytes) { + return Ok(Self::Deneb(payload)); + } + + if let Ok(payload) = ExecutionPayloadCapella::from_ssz_bytes(bytes) { + return Ok(Self::Capella(payload)); + } + + ExecutionPayloadBellatrix::from_ssz_bytes(bytes) + .map(Self::Bellatrix) .map_err(Into::into) } } diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 3ad6f1b94c6..13b0dfab9f7 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -16,7 +16,7 @@ use types::*; /// /// This can be deleted once schema versions prior to V22 are no longer supported. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes(derive(Debug, PartialEq, Clone, Encode, Decode)) )] #[derive(Debug, PartialEq, Clone, Encode)] @@ -68,9 +68,9 @@ where pub current_epoch_attestations: List, E::MaxPendingAttestations>, // Participation (Altair and later) - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] pub previous_epoch_participation: List, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] pub current_epoch_participation: List, // Finality @@ -80,13 +80,13 @@ where pub finalized_checkpoint: Checkpoint, // Inactivity - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] pub inactivity_scores: List, // Light-client sync committees - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] pub next_sync_committee: Arc>, // Execution @@ -115,39 +115,44 @@ where partial_getter(rename = "latest_execution_payload_header_fulu") )] pub latest_execution_payload_header: ExecutionPayloadHeaderFulu, + #[superstruct( + only(Gloas), + partial_getter(rename = "latest_execution_payload_header_gloas") + )] + pub latest_execution_payload_header: ExecutionPayloadHeaderGloas, // Capella - #[superstruct(only(Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))] pub next_withdrawal_index: u64, - #[superstruct(only(Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))] pub next_withdrawal_validator_index: u64, #[ssz(skip_serializing, skip_deserializing)] - #[superstruct(only(Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))] pub historical_summaries: Option>, // Electra - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub deposit_requests_start_index: u64, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub deposit_balance_to_consume: u64, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub exit_balance_to_consume: u64, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub earliest_exit_epoch: Epoch, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub consolidation_balance_to_consume: u64, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub earliest_consolidation_epoch: Epoch, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub pending_deposits: List, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub pending_partial_withdrawals: List, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub pending_consolidations: List, - #[superstruct(only(Fulu))] + #[superstruct(only(Fulu, Gloas))] pub proposer_lookahead: Vector, } @@ -450,6 +455,32 @@ impl TryInto> for PartialBeaconState { ], [historical_summaries] ), + PartialBeaconState::Gloas(inner) => impl_try_into_beacon_state!( + inner, + Gloas, + BeaconStateGloas, + [ + previous_epoch_participation, + current_epoch_participation, + current_sync_committee, + next_sync_committee, + inactivity_scores, + latest_execution_payload_header, + next_withdrawal_index, + next_withdrawal_validator_index, + deposit_requests_start_index, + deposit_balance_to_consume, + exit_balance_to_consume, + earliest_exit_epoch, + consolidation_balance_to_consume, + earliest_consolidation_epoch, + pending_deposits, + pending_partial_withdrawals, + pending_consolidations, + proposer_lookahead + ], + [historical_summaries] + ), }; Ok(state) } diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index c554831f8b4..169551e35bc 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -1140,7 +1140,7 @@ impl<'de> ContextDeserialize<'de, ForkName> for SsePayloadAttributes { ForkName::Capella => { Self::V2(Deserialize::deserialize(deserializer).map_err(convert_err)?) } - ForkName::Deneb | ForkName::Electra | ForkName::Fulu => { + ForkName::Deneb | ForkName::Electra | ForkName::Fulu | ForkName::Gloas => { Self::V3(Deserialize::deserialize(deserializer).map_err(convert_err)?) } }) @@ -2405,6 +2405,9 @@ mod test { rng, )), ExecutionPayload::Fulu(ExecutionPayloadFulu::::random_for_test(rng)), + ExecutionPayload::Gloas(ExecutionPayloadGloas::::random_for_test( + rng, + )), ]; let merged_forks = &ForkName::list_all()[2..]; assert_eq!( @@ -2459,6 +2462,17 @@ mod test { blobs_bundle, } }, + { + let execution_payload = + ExecutionPayload::Gloas( + ExecutionPayloadGloas::::random_for_test(rng), + ); + let blobs_bundle = BlobsBundle::random_for_test(rng); + ExecutionPayloadAndBlobs { + execution_payload, + blobs_bundle, + } + }, ]; let blob_forks = &ForkName::list_all()[4..]; diff --git a/common/eth2_network_config/built_in_network_configs/chiado/config.yaml b/common/eth2_network_config/built_in_network_configs/chiado/config.yaml index f0c833612d6..6bc41113d6f 100644 --- a/common/eth2_network_config/built_in_network_configs/chiado/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/chiado/config.yaml @@ -50,6 +50,9 @@ ELECTRA_FORK_EPOCH: 948224 # Thu Mar 6 2025 09:43:40 GMT+0000 # Fulu FULU_FORK_VERSION: 0x0600006f FULU_FORK_EPOCH: 18446744073709551615 +# Gloas +GLOAS_FORK_VERSION: 0x0700006f +GLOAS_FORK_EPOCH: 18446744073709551615 # Time parameters # --------------------------------------------------------------- @@ -154,3 +157,4 @@ DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 MAX_BLOBS_PER_BLOCK_FULU: 12 +# Gloas diff --git a/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml b/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml index 62c6d0db1bb..aa2dbb35d35 100644 --- a/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml @@ -47,6 +47,9 @@ ELECTRA_FORK_EPOCH: 1337856 # 2025-04-30T14:03:40.000Z # Fulu FULU_FORK_VERSION: 0x06000064 FULU_FORK_EPOCH: 18446744073709551615 +# Gloas +GLOAS_FORK_VERSION: 0x07000064 +GLOAS_FORK_EPOCH: 18446744073709551615 # Time parameters # --------------------------------------------------------------- @@ -138,3 +141,5 @@ DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 MAX_BLOBS_PER_BLOCK_FULU: 12 + +# Gloas \ No newline at end of file diff --git a/common/eth2_network_config/built_in_network_configs/holesky/config.yaml b/common/eth2_network_config/built_in_network_configs/holesky/config.yaml index 654689fdf16..ab5f0f3bde0 100644 --- a/common/eth2_network_config/built_in_network_configs/holesky/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/holesky/config.yaml @@ -39,6 +39,9 @@ ELECTRA_FORK_EPOCH: 115968 # Fulu FULU_FORK_VERSION: 0x07017000 FULU_FORK_EPOCH: 18446744073709551615 +# Gloas +GLOAS_FORK_VERSION: 0x08017000 +GLOAS_FORK_EPOCH: 18446744073709551615 # Time parameters # --------------------------------------------------------------- @@ -145,4 +148,6 @@ SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 VALIDATOR_CUSTODY_REQUIREMENT: 8 BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 -MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 \ No newline at end of file +MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 + +# Gloas \ No newline at end of file diff --git a/common/eth2_network_config/built_in_network_configs/hoodi/config.yaml b/common/eth2_network_config/built_in_network_configs/hoodi/config.yaml index ba4501ccfb2..01322974c8e 100644 --- a/common/eth2_network_config/built_in_network_configs/hoodi/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/hoodi/config.yaml @@ -44,6 +44,10 @@ ELECTRA_FORK_EPOCH: 2048 FULU_FORK_VERSION: 0x70000910 FULU_FORK_EPOCH: 18446744073709551615 +# Gloas +GLOAS_FORK_VERSION: 0x80000910 +GLOAS_FORK_EPOCH: 18446744073709551615 + # Time parameters # --------------------------------------------------------------- @@ -159,5 +163,7 @@ VALIDATOR_CUSTODY_REQUIREMENT: 8 BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 +# Gloas + # EIP7732 MAX_REQUEST_PAYLOADS: 128 diff --git a/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml b/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml index 72ace023d72..ca7f85b5122 100644 --- a/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml @@ -53,6 +53,9 @@ ELECTRA_FORK_EPOCH: 364032 # May 7, 2025, 10:05:11am UTC # Fulu FULU_FORK_VERSION: 0x06000000 FULU_FORK_EPOCH: 18446744073709551615 +# Gloas +GLOAS_FORK_VERSION: 0x07000000 +GLOAS_FORK_EPOCH: 18446744073709551615 # Time parameters # --------------------------------------------------------------- @@ -161,3 +164,5 @@ CUSTODY_REQUIREMENT: 4 VALIDATOR_CUSTODY_REQUIREMENT: 8 BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 + +# Gloas \ No newline at end of file diff --git a/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml b/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml index 013cddebe26..9802e409fbf 100644 --- a/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml @@ -40,6 +40,14 @@ DENEB_FORK_EPOCH: 132608 ELECTRA_FORK_VERSION: 0x90000074 ELECTRA_FORK_EPOCH: 222464 +# Fulu +FULU_FORK_VERSION: 0x90000075 +FULU_FORK_EPOCH: 18446744073709551615 + +# Gloas +GLOAS_FORK_VERSION: 0x90000076 +GLOAS_FORK_EPOCH: 18446744073709551615 + # Time parameters # --------------------------------------------------------------- # 12 seconds @@ -147,3 +155,5 @@ CUSTODY_REQUIREMENT: 4 VALIDATOR_CUSTODY_REQUIREMENT: 8 BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 + +# Gloas \ No newline at end of file diff --git a/consensus/state_processing/src/genesis.rs b/consensus/state_processing/src/genesis.rs index 8e62427ef13..88ef79310dc 100644 --- a/consensus/state_processing/src/genesis.rs +++ b/consensus/state_processing/src/genesis.rs @@ -5,6 +5,7 @@ use crate::common::DepositDataTree; use crate::upgrade::electra::upgrade_state_to_electra; use crate::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, upgrade_to_fulu, + upgrade_to_gloas, }; use safe_arith::{ArithError, SafeArith}; use std::sync::Arc; @@ -150,11 +151,27 @@ pub fn initialize_beacon_state_from_eth1( state.fork_mut().previous_version = spec.fulu_fork_version; // Override latest execution payload header. - if let Some(ExecutionPayloadHeader::Fulu(header)) = execution_payload_header { + if let Some(ExecutionPayloadHeader::Fulu(ref header)) = execution_payload_header { *state.latest_execution_payload_header_fulu_mut()? = header.clone(); } } + // Upgrade to gloas if configured from genesis. + if spec + .gloas_fork_epoch + .is_some_and(|fork_epoch| fork_epoch == E::genesis_epoch()) + { + upgrade_to_gloas(&mut state, spec)?; + + // Remove intermediate Fulu fork from `state.fork`. + state.fork_mut().previous_version = spec.gloas_fork_version; + + // Override latest execution payload header. + if let Some(ExecutionPayloadHeader::Gloas(header)) = execution_payload_header { + *state.latest_execution_payload_header_gloas_mut()? = header.clone(); + } + } + // Now that we have our validators, initialize the caches (including the committees) state.build_caches(spec)?; diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index 93892ecf5b6..99abbef9c1e 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -452,6 +452,12 @@ pub fn process_execution_payload>( _ => return Err(BlockProcessingError::IncorrectStateType), } } + ExecutionPayloadHeaderRefMut::Gloas(header_mut) => { + match payload.to_execution_payload_header() { + ExecutionPayloadHeader::Gloas(header) => *header_mut = header, + _ => return Err(BlockProcessingError::IncorrectStateType), + } + } } Ok(()) diff --git a/consensus/state_processing/src/per_slot_processing.rs b/consensus/state_processing/src/per_slot_processing.rs index 7fccf78c874..04b1e8148f6 100644 --- a/consensus/state_processing/src/per_slot_processing.rs +++ b/consensus/state_processing/src/per_slot_processing.rs @@ -1,6 +1,6 @@ use crate::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, - upgrade_to_electra, upgrade_to_fulu, + upgrade_to_electra, upgrade_to_fulu, upgrade_to_gloas, }; use crate::{per_epoch_processing::EpochProcessingSummary, *}; use safe_arith::{ArithError, SafeArith}; @@ -78,6 +78,11 @@ pub fn per_slot_processing( upgrade_to_fulu(state, spec)?; } + // Gloas. + if spec.gloas_fork_epoch == Some(state.current_epoch()) { + upgrade_to_gloas(state, spec)?; + } + // Additionally build all caches so that all valid states that are advanced always have // committee caches built, and we don't have to worry about initialising them at higher // layers. diff --git a/consensus/state_processing/src/upgrade.rs b/consensus/state_processing/src/upgrade.rs index 88bc87849f3..d175c3ae408 100644 --- a/consensus/state_processing/src/upgrade.rs +++ b/consensus/state_processing/src/upgrade.rs @@ -4,6 +4,7 @@ pub mod capella; pub mod deneb; pub mod electra; pub mod fulu; +pub mod gloas; pub use altair::upgrade_to_altair; pub use bellatrix::upgrade_to_bellatrix; @@ -11,3 +12,4 @@ pub use capella::upgrade_to_capella; pub use deneb::upgrade_to_deneb; pub use electra::upgrade_to_electra; pub use fulu::upgrade_to_fulu; +pub use gloas::upgrade_to_gloas; diff --git a/consensus/state_processing/src/upgrade/gloas.rs b/consensus/state_processing/src/upgrade/gloas.rs new file mode 100644 index 00000000000..8bb6991bfbe --- /dev/null +++ b/consensus/state_processing/src/upgrade/gloas.rs @@ -0,0 +1,93 @@ +use std::mem; +use types::{BeaconState, BeaconStateError as Error, BeaconStateGloas, ChainSpec, EthSpec, Fork}; + +/// Transform a `Fulu` state into a `Gloas` state. +pub fn upgrade_to_gloas( + pre_state: &mut BeaconState, + spec: &ChainSpec, +) -> Result<(), Error> { + let post = upgrade_state_to_gloas(pre_state, spec)?; + + *pre_state = post; + + Ok(()) +} + +pub fn upgrade_state_to_gloas( + pre_state: &mut BeaconState, + spec: &ChainSpec, +) -> Result, Error> { + let epoch = pre_state.current_epoch(); + let pre = pre_state.as_fulu_mut()?; + // Where possible, use something like `mem::take` to move fields from behind the &mut + // reference. For other fields that don't have a good default value, use `clone`. + // + // Fixed size vectors get cloned because replacing them would require the same size + // allocation as cloning. + let post = BeaconState::Gloas(BeaconStateGloas { + // Versioning + genesis_time: pre.genesis_time, + genesis_validators_root: pre.genesis_validators_root, + slot: pre.slot, + fork: Fork { + previous_version: pre.fork.current_version, + current_version: spec.gloas_fork_version, + epoch, + }, + // History + latest_block_header: pre.latest_block_header.clone(), + block_roots: pre.block_roots.clone(), + state_roots: pre.state_roots.clone(), + historical_roots: mem::take(&mut pre.historical_roots), + // Eth1 + eth1_data: pre.eth1_data.clone(), + eth1_data_votes: mem::take(&mut pre.eth1_data_votes), + eth1_deposit_index: pre.eth1_deposit_index, + // Registry + validators: mem::take(&mut pre.validators), + balances: mem::take(&mut pre.balances), + // Randomness + randao_mixes: pre.randao_mixes.clone(), + // Slashings + slashings: pre.slashings.clone(), + // `Participation + previous_epoch_participation: mem::take(&mut pre.previous_epoch_participation), + current_epoch_participation: mem::take(&mut pre.current_epoch_participation), + // Finality + justification_bits: pre.justification_bits.clone(), + previous_justified_checkpoint: pre.previous_justified_checkpoint, + current_justified_checkpoint: pre.current_justified_checkpoint, + finalized_checkpoint: pre.finalized_checkpoint, + // Inactivity + inactivity_scores: mem::take(&mut pre.inactivity_scores), + // Sync committees + current_sync_committee: pre.current_sync_committee.clone(), + next_sync_committee: pre.next_sync_committee.clone(), + // Execution + latest_execution_payload_header: pre.latest_execution_payload_header.upgrade_to_gloas(), + // Capella + next_withdrawal_index: pre.next_withdrawal_index, + next_withdrawal_validator_index: pre.next_withdrawal_validator_index, + historical_summaries: pre.historical_summaries.clone(), + // Electra + deposit_requests_start_index: pre.deposit_requests_start_index, + deposit_balance_to_consume: pre.deposit_balance_to_consume, + exit_balance_to_consume: pre.exit_balance_to_consume, + earliest_exit_epoch: pre.earliest_exit_epoch, + consolidation_balance_to_consume: pre.consolidation_balance_to_consume, + earliest_consolidation_epoch: pre.earliest_consolidation_epoch, + pending_deposits: pre.pending_deposits.clone(), + pending_partial_withdrawals: pre.pending_partial_withdrawals.clone(), + pending_consolidations: pre.pending_consolidations.clone(), + // Caches + total_active_balance: pre.total_active_balance, + progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache), + committee_caches: mem::take(&mut pre.committee_caches), + pubkey_cache: mem::take(&mut pre.pubkey_cache), + exit_cache: mem::take(&mut pre.exit_cache), + slashings_cache: mem::take(&mut pre.slashings_cache), + epoch_cache: mem::take(&mut pre.epoch_cache), + proposer_lookahead: mem::take(&mut pre.proposer_lookahead), + }); + Ok(post) +} diff --git a/consensus/types/presets/gnosis/gloas.yaml b/consensus/types/presets/gnosis/gloas.yaml new file mode 100644 index 00000000000..170accaac3a --- /dev/null +++ b/consensus/types/presets/gnosis/gloas.yaml @@ -0,0 +1 @@ +# Gnosis preset - Gloas diff --git a/consensus/types/presets/mainnet/gloas.yaml b/consensus/types/presets/mainnet/gloas.yaml new file mode 100644 index 00000000000..45b7b8ae968 --- /dev/null +++ b/consensus/types/presets/mainnet/gloas.yaml @@ -0,0 +1 @@ +# Mainnet preset - Gloas diff --git a/consensus/types/presets/minimal/gloas.yaml b/consensus/types/presets/minimal/gloas.yaml new file mode 100644 index 00000000000..51b3f048571 --- /dev/null +++ b/consensus/types/presets/minimal/gloas.yaml @@ -0,0 +1 @@ +# Minimal preset - Gloas diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index 9494838c006..f4e4e369661 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -16,7 +16,7 @@ use self::indexed_attestation::IndexedAttestationBase; /// A block of the `BeaconChain`. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -82,6 +82,8 @@ pub struct BeaconBlock = FullPayload pub body: BeaconBlockBodyElectra, #[superstruct(only(Fulu), partial_getter(rename = "body_fulu"))] pub body: BeaconBlockBodyFulu, + #[superstruct(only(Gloas), partial_getter(rename = "body_gloas"))] + pub body: BeaconBlockBodyGloas, } pub type BlindedBeaconBlock = BeaconBlock>; @@ -134,8 +136,9 @@ impl> BeaconBlock { /// Usually it's better to prefer `from_ssz_bytes` which will decode the correct variant based /// on the fork slot. pub fn any_from_ssz_bytes(bytes: &[u8]) -> Result { - BeaconBlockFulu::from_ssz_bytes(bytes) - .map(BeaconBlock::Fulu) + BeaconBlockGloas::from_ssz_bytes(bytes) + .map(BeaconBlock::Gloas) + .or_else(|_| BeaconBlockFulu::from_ssz_bytes(bytes).map(BeaconBlock::Fulu)) .or_else(|_| BeaconBlockElectra::from_ssz_bytes(bytes).map(BeaconBlock::Electra)) .or_else(|_| BeaconBlockDeneb::from_ssz_bytes(bytes).map(BeaconBlock::Deneb)) .or_else(|_| BeaconBlockCapella::from_ssz_bytes(bytes).map(BeaconBlock::Capella)) @@ -235,6 +238,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockRef<'a, E, Payl BeaconBlockRef::Deneb { .. } => ForkName::Deneb, BeaconBlockRef::Electra { .. } => ForkName::Electra, BeaconBlockRef::Fulu { .. } => ForkName::Fulu, + BeaconBlockRef::Gloas { .. } => ForkName::Gloas, } } @@ -646,6 +650,37 @@ impl> EmptyBlock for BeaconBlockFulu } } +impl> EmptyBlock for BeaconBlockGloas { + /// Returns an empty Gloas block to be used during genesis. + fn empty(spec: &ChainSpec) -> Self { + BeaconBlockGloas { + slot: spec.genesis_slot, + proposer_index: 0, + parent_root: Hash256::zero(), + state_root: Hash256::zero(), + body: BeaconBlockBodyGloas { + randao_reveal: Signature::empty(), + eth1_data: Eth1Data { + deposit_root: Hash256::zero(), + block_hash: Hash256::zero(), + deposit_count: 0, + }, + graffiti: Graffiti::default(), + proposer_slashings: VariableList::empty(), + attester_slashings: VariableList::empty(), + attestations: VariableList::empty(), + deposits: VariableList::empty(), + voluntary_exits: VariableList::empty(), + sync_aggregate: SyncAggregate::empty(), + execution_payload: Payload::Gloas::default(), + bls_to_execution_changes: VariableList::empty(), + blob_kzg_commitments: VariableList::empty(), + execution_requests: ExecutionRequests::default(), + }, + } + } +} + // We can convert pre-Bellatrix blocks without payloads into blocks "with" payloads. impl From>> for BeaconBlockBase> @@ -728,6 +763,7 @@ impl_from!(BeaconBlockCapella, >, >, |bod impl_from!(BeaconBlockDeneb, >, >, |body: BeaconBlockBodyDeneb<_, _>| body.into()); impl_from!(BeaconBlockElectra, >, >, |body: BeaconBlockBodyElectra<_, _>| body.into()); impl_from!(BeaconBlockFulu, >, >, |body: BeaconBlockBodyFulu<_, _>| body.into()); +impl_from!(BeaconBlockGloas, >, >, |body: BeaconBlockBodyGloas<_, _>| body.into()); // We can clone blocks with payloads to blocks without payloads, without cloning the payload. macro_rules! impl_clone_as_blinded { @@ -762,6 +798,7 @@ impl_clone_as_blinded!(BeaconBlockCapella, >, >, >); impl_clone_as_blinded!(BeaconBlockElectra, >, >); impl_clone_as_blinded!(BeaconBlockFulu, >, >); +impl_clone_as_blinded!(BeaconBlockGloas, >, >); // A reference to a full beacon block can be cloned into a blinded beacon block, without cloning the // execution payload. @@ -950,6 +987,26 @@ mod tests { }); } + #[test] + fn roundtrip_gloas_block() { + let rng = &mut XorShiftRng::from_seed([42; 16]); + let spec = &ForkName::Gloas.make_genesis_spec(MainnetEthSpec::default_spec()); + + let inner_block = BeaconBlockGloas { + slot: Slot::random_for_test(rng), + proposer_index: u64::random_for_test(rng), + parent_root: Hash256::random_for_test(rng), + state_root: Hash256::random_for_test(rng), + body: BeaconBlockBodyGloas::random_for_test(rng), + }; + + let block = BeaconBlock::Gloas(inner_block.clone()); + + test_ssz_tree_hash_pair_with(&block, &inner_block, |bytes| { + BeaconBlock::from_ssz_bytes(bytes, spec) + }); + } + #[test] fn decode_base_and_altair() { type E = MainnetEthSpec; @@ -971,12 +1028,15 @@ mod tests { let electra_slot = electra_epoch.start_slot(E::slots_per_epoch()); let fulu_epoch = electra_epoch + 1; let fulu_slot = fulu_epoch.start_slot(E::slots_per_epoch()); + let gloas_epoch = fulu_epoch + 1; + let gloas_slot = gloas_epoch.start_slot(E::slots_per_epoch()); spec.altair_fork_epoch = Some(altair_epoch); spec.capella_fork_epoch = Some(capella_epoch); spec.deneb_fork_epoch = Some(deneb_epoch); spec.electra_fork_epoch = Some(electra_epoch); spec.fulu_fork_epoch = Some(fulu_epoch); + spec.gloas_fork_epoch = Some(gloas_epoch); // BeaconBlockBase { @@ -1101,5 +1161,30 @@ mod tests { good_block ); } + + // BeaconBlockGloas + { + let good_block = BeaconBlock::Gloas(BeaconBlockGloas { + slot: gloas_slot, + ..<_>::random_for_test(rng) + }); + // It's invalid to have a Fulu block with a epoch lower than the fork epoch. + let _bad_block = { + let mut bad = good_block.clone(); + *bad.slot_mut() = fulu_slot; + bad + }; + + assert_eq!( + BeaconBlock::from_ssz_bytes(&good_block.as_ssz_bytes(), &spec) + .expect("good gloas block can be decoded"), + good_block + ); + + // TODO(gloas): Uncomment once Gloas has features since without features + // and with a Fulu slot it decodes successfully to Fulu. + //BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec) + // .expect_err("bad gloas block cannot be decoded"); + } } } diff --git a/consensus/types/src/beacon_block_body.rs b/consensus/types/src/beacon_block_body.rs index 5538e7c45c8..73d02dbe614 100644 --- a/consensus/types/src/beacon_block_body.rs +++ b/consensus/types/src/beacon_block_body.rs @@ -28,7 +28,7 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11; /// /// This *superstruct* abstracts over the hard-fork. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -60,7 +60,8 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11; Capella(metastruct(mappings(beacon_block_body_capella_fields(groups(fields))))), Deneb(metastruct(mappings(beacon_block_body_deneb_fields(groups(fields))))), Electra(metastruct(mappings(beacon_block_body_electra_fields(groups(fields))))), - Fulu(metastruct(mappings(beacon_block_body_fulu_fields(groups(fields))))) + Fulu(metastruct(mappings(beacon_block_body_fulu_fields(groups(fields))))), + Gloas(metastruct(mappings(beacon_block_body_gloas_fields(groups(fields))))), ), cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"), partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant") @@ -86,7 +87,7 @@ pub struct BeaconBlockBody = FullPay )] pub attester_slashings: VariableList, E::MaxAttesterSlashings>, #[superstruct( - only(Electra, Fulu), + only(Electra, Fulu, Gloas), partial_getter(rename = "attester_slashings_electra") )] pub attester_slashings: @@ -96,11 +97,14 @@ pub struct BeaconBlockBody = FullPay partial_getter(rename = "attestations_base") )] pub attestations: VariableList, E::MaxAttestations>, - #[superstruct(only(Electra, Fulu), partial_getter(rename = "attestations_electra"))] + #[superstruct( + only(Electra, Fulu, Gloas), + partial_getter(rename = "attestations_electra") + )] pub attestations: VariableList, E::MaxAttestationsElectra>, pub deposits: VariableList, pub voluntary_exits: VariableList, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] pub sync_aggregate: SyncAggregate, // We flatten the execution payload so that serde can use the name of the inner type, // either `execution_payload` for full payloads, or `execution_payload_header` for blinded @@ -123,12 +127,15 @@ pub struct BeaconBlockBody = FullPay #[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))] #[serde(flatten)] pub execution_payload: Payload::Fulu, - #[superstruct(only(Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))] + #[serde(flatten)] + pub execution_payload: Payload::Gloas, + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))] pub bls_to_execution_changes: VariableList, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] pub blob_kzg_commitments: KzgCommitments, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub execution_requests: ExecutionRequests, #[superstruct(only(Base, Altair))] #[metastruct(exclude_from(fields))] @@ -159,6 +166,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, Self::Deneb(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Electra(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Fulu(body) => Ok(Payload::Ref::from(&body.execution_payload)), + Self::Gloas(body) => Ok(Payload::Ref::from(&body.execution_payload)), } } @@ -193,6 +201,10 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, beacon_block_body_fulu_fields!(body, |_, field| leaves .push(field.tree_hash_root())); } + Self::Gloas(body) => { + beacon_block_body_gloas_fields!(body, |_, field| leaves + .push(field.tree_hash_root())); + } } leaves } @@ -221,7 +233,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, Self::Base(_) | Self::Altair(_) | Self::Bellatrix(_) | Self::Capella(_) => { Err(Error::IncorrectStateVariant) } - Self::Deneb(_) | Self::Electra(_) | Self::Fulu(_) => { + Self::Deneb(_) | Self::Electra(_) | Self::Fulu(_) | Self::Gloas(_) => { // We compute the branches by generating 2 merkle trees: // 1. Merkle tree for the `blob_kzg_commitments` List object // 2. Merkle tree for the `BeaconBlockBody` container @@ -314,6 +326,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, Self::Deneb(body) => body.attestations.len(), Self::Electra(body) => body.attestations.len(), Self::Fulu(body) => body.attestations.len(), + Self::Gloas(body) => body.attestations.len(), } } @@ -326,6 +339,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, Self::Deneb(body) => body.attester_slashings.len(), Self::Electra(body) => body.attester_slashings.len(), Self::Fulu(body) => body.attester_slashings.len(), + Self::Gloas(body) => body.attester_slashings.len(), } } @@ -338,6 +352,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, Self::Deneb(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), Self::Electra(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)), Self::Fulu(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)), + Self::Gloas(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)), } } @@ -378,6 +393,11 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, .iter() .map(AttesterSlashingRef::Electra), ), + Self::Gloas(body) => Box::new( + body.attester_slashings + .iter() + .map(AttesterSlashingRef::Electra), + ), } } } @@ -406,6 +426,9 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRefMut<'a, Self::Fulu(body) => { Box::new(body.attestations.iter_mut().map(AttestationRefMut::Electra)) } + Self::Gloas(body) => { + Box::new(body.attestations.iter_mut().map(AttestationRefMut::Electra)) + } } } } @@ -421,6 +444,7 @@ impl> BeaconBlockBodyRef<'_, E, Payl BeaconBlockBodyRef::Deneb { .. } => ForkName::Deneb, BeaconBlockBodyRef::Electra { .. } => ForkName::Electra, BeaconBlockBodyRef::Fulu { .. } => ForkName::Fulu, + BeaconBlockBodyRef::Gloas { .. } => ForkName::Gloas, } } } @@ -781,6 +805,52 @@ impl From>> } } +impl From>> + for ( + BeaconBlockBodyGloas>, + Option>, + ) +{ + fn from(body: BeaconBlockBodyGloas>) -> Self { + let BeaconBlockBodyGloas { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadGloas { execution_payload }, + bls_to_execution_changes, + blob_kzg_commitments, + execution_requests, + } = body; + + ( + BeaconBlockBodyGloas { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: BlindedPayloadGloas { + execution_payload_header: From::from(&execution_payload), + }, + bls_to_execution_changes, + blob_kzg_commitments: blob_kzg_commitments.clone(), + execution_requests, + }, + Some(execution_payload), + ) + } +} + // We can clone a full block into a blinded block, without cloning the payload. impl BeaconBlockBodyBase> { pub fn clone_as_blinded(&self) -> BeaconBlockBodyBase> { @@ -974,6 +1044,44 @@ impl BeaconBlockBodyFulu> { } } +impl BeaconBlockBodyGloas> { + pub fn clone_as_blinded(&self) -> BeaconBlockBodyGloas> { + let BeaconBlockBodyGloas { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadGloas { execution_payload }, + bls_to_execution_changes, + blob_kzg_commitments, + execution_requests, + } = self; + + BeaconBlockBodyGloas { + randao_reveal: randao_reveal.clone(), + eth1_data: eth1_data.clone(), + graffiti: *graffiti, + proposer_slashings: proposer_slashings.clone(), + attester_slashings: attester_slashings.clone(), + attestations: attestations.clone(), + deposits: deposits.clone(), + voluntary_exits: voluntary_exits.clone(), + sync_aggregate: sync_aggregate.clone(), + execution_payload: BlindedPayloadGloas { + execution_payload_header: execution_payload.into(), + }, + bls_to_execution_changes: bls_to_execution_changes.clone(), + blob_kzg_commitments: blob_kzg_commitments.clone(), + execution_requests: execution_requests.clone(), + } + } +} + impl From>> for ( BeaconBlockBody>, diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index c27200c8942..5f3dff56cc8 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -229,7 +229,7 @@ impl From for Hash256 { /// /// https://github.com/sigp/milhouse/issues/43 #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Derivative, @@ -349,6 +349,20 @@ impl From for Hash256 { groups(tree_lists) )), num_fields(all()), + )), + Gloas(metastruct( + mappings( + map_beacon_state_gloas_fields(), + map_beacon_state_gloas_tree_list_fields(mutable, fallible, groups(tree_lists)), + map_beacon_state_gloas_tree_list_fields_immutable(groups(tree_lists)), + ), + bimappings(bimap_beacon_state_gloas_tree_list_fields( + other_type = "BeaconStateGloas", + self_mutable, + fallible, + groups(tree_lists) + )), + num_fields(all()), )) ), cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"), @@ -435,11 +449,11 @@ where // Participation (Altair and later) #[compare_fields(as_iter)] - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] #[test_random(default)] #[compare_fields(as_iter)] pub previous_epoch_participation: List, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] #[test_random(default)] pub current_epoch_participation: List, @@ -459,15 +473,15 @@ where // Inactivity #[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")] - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] #[test_random(default)] pub inactivity_scores: List, // Light-client sync committees - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] #[metastruct(exclude_from(tree_lists))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas))] #[metastruct(exclude_from(tree_lists))] pub next_sync_committee: Arc>, @@ -502,64 +516,72 @@ where )] #[metastruct(exclude_from(tree_lists))] pub latest_execution_payload_header: ExecutionPayloadHeaderFulu, + #[superstruct( + only(Gloas), + partial_getter(rename = "latest_execution_payload_header_gloas") + )] + #[metastruct(exclude_from(tree_lists))] + pub latest_execution_payload_header: ExecutionPayloadHeaderGloas, // Capella - #[superstruct(only(Capella, Deneb, Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] #[metastruct(exclude_from(tree_lists))] pub next_withdrawal_index: u64, - #[superstruct(only(Capella, Deneb, Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] #[metastruct(exclude_from(tree_lists))] pub next_withdrawal_validator_index: u64, // Deep history valid from Capella onwards. - #[superstruct(only(Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))] #[test_random(default)] pub historical_summaries: List, // Electra - #[superstruct(only(Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Electra, Fulu, Gloas), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] #[serde(with = "serde_utils::quoted_u64")] pub deposit_requests_start_index: u64, - #[superstruct(only(Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Electra, Fulu, Gloas), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] #[serde(with = "serde_utils::quoted_u64")] pub deposit_balance_to_consume: u64, - #[superstruct(only(Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Electra, Fulu, Gloas), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] #[serde(with = "serde_utils::quoted_u64")] pub exit_balance_to_consume: u64, - #[superstruct(only(Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Electra, Fulu, Gloas), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] pub earliest_exit_epoch: Epoch, - #[superstruct(only(Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Electra, Fulu, Gloas), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] #[serde(with = "serde_utils::quoted_u64")] pub consolidation_balance_to_consume: u64, - #[superstruct(only(Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Electra, Fulu, Gloas), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] pub earliest_consolidation_epoch: Epoch, #[compare_fields(as_iter)] #[test_random(default)] - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub pending_deposits: List, #[compare_fields(as_iter)] #[test_random(default)] - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub pending_partial_withdrawals: List, #[compare_fields(as_iter)] #[test_random(default)] - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub pending_consolidations: List, // Fulu #[compare_fields(as_iter)] #[test_random(default)] - #[superstruct(only(Fulu))] + #[superstruct(only(Fulu, Gloas))] pub proposer_lookahead: Vector, + // Gloas + // Caching (not in the spec) #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing, skip_deserializing)] @@ -699,6 +721,7 @@ impl BeaconState { BeaconState::Deneb { .. } => ForkName::Deneb, BeaconState::Electra { .. } => ForkName::Electra, BeaconState::Fulu { .. } => ForkName::Fulu, + BeaconState::Gloas { .. } => ForkName::Gloas, } } @@ -1048,6 +1071,9 @@ impl BeaconState { BeaconState::Fulu(state) => Ok(ExecutionPayloadHeaderRef::Fulu( &state.latest_execution_payload_header, )), + BeaconState::Gloas(state) => Ok(ExecutionPayloadHeaderRef::Gloas( + &state.latest_execution_payload_header, + )), } } @@ -1071,6 +1097,9 @@ impl BeaconState { BeaconState::Fulu(state) => Ok(ExecutionPayloadHeaderRefMut::Fulu( &mut state.latest_execution_payload_header, )), + BeaconState::Gloas(state) => Ok(ExecutionPayloadHeaderRefMut::Gloas( + &mut state.latest_execution_payload_header, + )), } } @@ -1616,6 +1645,16 @@ impl BeaconState { &mut state.exit_cache, &mut state.epoch_cache, )), + BeaconState::Gloas(state) => Ok(( + &mut state.validators, + &mut state.balances, + &state.previous_epoch_participation, + &state.current_epoch_participation, + &mut state.inactivity_scores, + &mut state.progressive_balances_cache, + &mut state.exit_cache, + &mut state.epoch_cache, + )), } } @@ -1797,12 +1836,13 @@ impl BeaconState { | BeaconState::Altair(_) | BeaconState::Bellatrix(_) | BeaconState::Capella(_) => self.get_validator_churn_limit(spec)?, - BeaconState::Deneb(_) | BeaconState::Electra(_) | BeaconState::Fulu(_) => { - std::cmp::min( - spec.max_per_epoch_activation_churn_limit, - self.get_validator_churn_limit(spec)?, - ) - } + BeaconState::Deneb(_) + | BeaconState::Electra(_) + | BeaconState::Fulu(_) + | BeaconState::Gloas(_) => std::cmp::min( + spec.max_per_epoch_activation_churn_limit, + self.get_validator_churn_limit(spec)?, + ), }) } @@ -1922,6 +1962,7 @@ impl BeaconState { BeaconState::Deneb(state) => Ok(&mut state.current_epoch_participation), BeaconState::Electra(state) => Ok(&mut state.current_epoch_participation), BeaconState::Fulu(state) => Ok(&mut state.current_epoch_participation), + BeaconState::Gloas(state) => Ok(&mut state.current_epoch_participation), } } else if epoch == previous_epoch { match self { @@ -1932,6 +1973,7 @@ impl BeaconState { BeaconState::Deneb(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Electra(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Fulu(state) => Ok(&mut state.previous_epoch_participation), + BeaconState::Gloas(state) => Ok(&mut state.previous_epoch_participation), } } else { Err(BeaconStateError::EpochOutOfBounds) @@ -2196,6 +2238,11 @@ impl BeaconState { any_pending_mutations |= self_field.has_pending_updates(); }); } + Self::Gloas(self_inner) => { + map_beacon_state_gloas_tree_list_fields_immutable!(self_inner, |_, self_field| { + any_pending_mutations |= self_field.has_pending_updates(); + }); + } }; any_pending_mutations } @@ -2396,7 +2443,7 @@ impl BeaconState { | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) => Err(Error::IncorrectStateVariant), - BeaconState::Electra(_) | BeaconState::Fulu(_) => { + BeaconState::Electra(_) | BeaconState::Fulu(_) | BeaconState::Gloas(_) => { // Consume the balance and update state variables *self.exit_balance_to_consume_mut()? = exit_balance_to_consume.safe_sub(exit_balance)?; @@ -2443,7 +2490,7 @@ impl BeaconState { | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) => Err(Error::IncorrectStateVariant), - BeaconState::Electra(_) | BeaconState::Fulu(_) => { + BeaconState::Electra(_) | BeaconState::Fulu(_) | BeaconState::Gloas(_) => { // Consume the balance and update state variables. *self.consolidation_balance_to_consume_mut()? = consolidation_balance_to_consume.safe_sub(consolidation_balance)?; @@ -2514,6 +2561,14 @@ impl BeaconState { ); } (Self::Fulu(_), _) => (), + (Self::Gloas(self_inner), Self::Gloas(base_inner)) => { + bimap_beacon_state_gloas_tree_list_fields!( + self_inner, + base_inner, + |_, self_field, base_field| { self_field.rebase_on(base_field) } + ); + } + (Self::Gloas(_), _) => (), } // Use sync committees from `base` if they are equal. @@ -2591,6 +2646,7 @@ impl BeaconState { ForkName::Deneb => BeaconStateDeneb::::NUM_FIELDS.next_power_of_two(), ForkName::Electra => BeaconStateElectra::::NUM_FIELDS.next_power_of_two(), ForkName::Fulu => BeaconStateFulu::::NUM_FIELDS.next_power_of_two(), + ForkName::Gloas => BeaconStateGloas::::NUM_FIELDS.next_power_of_two(), } } @@ -2642,6 +2698,9 @@ impl BeaconState { Self::Fulu(inner) => { map_beacon_state_fulu_tree_list_fields!(inner, |_, x| { x.apply_updates() }) } + Self::Gloas(inner) => { + map_beacon_state_gloas_tree_list_fields!(inner, |_, x| { x.apply_updates() }) + } } Ok(()) } @@ -2755,6 +2814,11 @@ impl BeaconState { leaves.push(field.tree_hash_root()); }); } + BeaconState::Gloas(state) => { + map_beacon_state_gloas_fields!(state, |_, field| { + leaves.push(field.tree_hash_root()); + }); + } }; leaves @@ -2813,6 +2877,7 @@ impl CompareFields for BeaconState { (BeaconState::Deneb(x), BeaconState::Deneb(y)) => x.compare_fields(y), (BeaconState::Electra(x), BeaconState::Electra(y)) => x.compare_fields(y), (BeaconState::Fulu(x), BeaconState::Fulu(y)) => x.compare_fields(y), + (BeaconState::Gloas(x), BeaconState::Gloas(y)) => x.compare_fields(y), _ => panic!("compare_fields: mismatched state variants",), } } diff --git a/consensus/types/src/builder_bid.rs b/consensus/types/src/builder_bid.rs index ee157d50138..3fb7af35ca1 100644 --- a/consensus/types/src/builder_bid.rs +++ b/consensus/types/src/builder_bid.rs @@ -2,8 +2,9 @@ use crate::beacon_block_body::KzgCommitments; use crate::{ ChainSpec, ContextDeserialize, EthSpec, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, - ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, - ExecutionRequests, ForkName, ForkVersionDecode, SignedRoot, Uint256, test_utils::TestRandom, + ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, + ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionDecode, SignedRoot, + Uint256, test_utils::TestRandom, }; use bls::PublicKeyBytes; use bls::Signature; @@ -15,7 +16,7 @@ use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( PartialEq, @@ -48,9 +49,11 @@ pub struct BuilderBid { pub header: ExecutionPayloadHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "header_fulu"))] pub header: ExecutionPayloadHeaderFulu, - #[superstruct(only(Deneb, Electra, Fulu))] + #[superstruct(only(Gloas), partial_getter(rename = "header_gloas"))] + pub header: ExecutionPayloadHeaderGloas, + #[superstruct(only(Deneb, Electra, Fulu, Gloas))] pub blob_kzg_commitments: KzgCommitments, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] pub execution_requests: ExecutionRequests, #[serde(with = "serde_utils::quoted_u256")] pub value: Uint256, @@ -95,6 +98,7 @@ impl ForkVersionDecode for BuilderBid { ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb::from_ssz_bytes(bytes)?), ForkName::Electra => BuilderBid::Electra(BuilderBidElectra::from_ssz_bytes(bytes)?), ForkName::Fulu => BuilderBid::Fulu(BuilderBidFulu::from_ssz_bytes(bytes)?), + ForkName::Gloas => BuilderBid::Gloas(BuilderBidGloas::from_ssz_bytes(bytes)?), }; Ok(builder_bid) } @@ -150,6 +154,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for BuilderBid { ForkName::Fulu => { Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?) } + ForkName::Gloas => { + Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?) + } ForkName::Base | ForkName::Altair => { return Err(serde::de::Error::custom(format!( "BuilderBid failed to deserialize: unsupported fork '{}'", diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index f42159252a3..09ed70c8e4c 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -208,6 +208,13 @@ pub struct ChainSpec { pub validator_custody_requirement: u64, pub balance_per_additional_custody_group: u64, + /* + * Gloas hard fork params + */ + pub gloas_fork_version: [u8; 4], + /// The Gloas fork epoch is optional, with `None` representing "Gloas never happens". + pub gloas_fork_epoch: Option, + /* * Networking */ @@ -250,6 +257,10 @@ pub struct ChainSpec { pub(crate) blob_schedule: BlobSchedule, min_epochs_for_data_column_sidecars_requests: u64, + /* + * Networking Gloas + */ + /* * Networking Derived * @@ -321,25 +332,26 @@ impl ChainSpec { /// Returns the name of the fork which is active at `epoch`. pub fn fork_name_at_epoch(&self, epoch: Epoch) -> ForkName { - match self.fulu_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Fulu, - _ => match self.electra_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Electra, - _ => match self.deneb_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Deneb, - _ => match self.capella_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Capella, - _ => match self.bellatrix_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Bellatrix, - _ => match self.altair_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Altair, - _ => ForkName::Base, - }, - }, - }, - }, - }, + let forks = [ + (self.gloas_fork_epoch, ForkName::Gloas), + (self.fulu_fork_epoch, ForkName::Fulu), + (self.electra_fork_epoch, ForkName::Electra), + (self.deneb_fork_epoch, ForkName::Deneb), + (self.capella_fork_epoch, ForkName::Capella), + (self.bellatrix_fork_epoch, ForkName::Bellatrix), + (self.altair_fork_epoch, ForkName::Altair), + ]; + + // Find the first fork where `epoch` is >= `fork_epoch`. + for (fork_epoch_opt, fork_name) in forks.iter() { + if let Some(fork_epoch) = fork_epoch_opt + && epoch >= *fork_epoch + { + return *fork_name; + } } + + ForkName::Base } /// Returns the fork version for a named fork. @@ -352,6 +364,7 @@ impl ChainSpec { ForkName::Deneb => self.deneb_fork_version, ForkName::Electra => self.electra_fork_version, ForkName::Fulu => self.fulu_fork_version, + ForkName::Gloas => self.gloas_fork_version, } } @@ -370,6 +383,7 @@ impl ChainSpec { ForkName::Deneb => self.deneb_fork_epoch, ForkName::Electra => self.electra_fork_epoch, ForkName::Fulu => self.fulu_fork_epoch, + ForkName::Gloas => self.gloas_fork_epoch, } } @@ -453,6 +467,12 @@ impl ChainSpec { .is_some_and(|fulu_fork_epoch| fulu_fork_epoch != self.far_future_epoch) } + /// Returns true if `GLOAS_FORK_EPOCH` is set and is not set to `FAR_FUTURE_EPOCH`. + pub fn is_gloas_scheduled(&self) -> bool { + self.gloas_fork_epoch + .is_some_and(|gloas_fork_epoch| gloas_fork_epoch != self.far_future_epoch) + } + /// Returns a full `Fork` struct for a given epoch. pub fn fork_at_epoch(&self, epoch: Epoch) -> Fork { let current_fork_name = self.fork_name_at_epoch(epoch); @@ -1050,6 +1070,12 @@ impl ChainSpec { validator_custody_requirement: 8, balance_per_additional_custody_group: 32000000000, + /* + * Gloas hard fork params + */ + gloas_fork_version: [0x07, 0x00, 0x00, 0x00], + gloas_fork_epoch: None, + /* * Network specific */ @@ -1173,6 +1199,9 @@ impl ChainSpec { // Fulu fulu_fork_version: [0x06, 0x00, 0x00, 0x01], fulu_fork_epoch: None, + // Gloas + gloas_fork_version: [0x07, 0x00, 0x00, 0x00], + gloas_fork_epoch: None, // Other network_id: 2, // lighthouse testnet network id deposit_chain_id: 5, @@ -1387,6 +1416,12 @@ impl ChainSpec { validator_custody_requirement: 8, balance_per_additional_custody_group: 32000000000, + /* + * Gloas hard fork params + */ + gloas_fork_version: [0x07, 0x00, 0x00, 0x64], + gloas_fork_epoch: None, + /* * Network specific */ @@ -1648,6 +1683,14 @@ pub struct Config { #[serde(deserialize_with = "deserialize_fork_epoch")] pub fulu_fork_epoch: Option>, + #[serde(default = "default_gloas_fork_version")] + #[serde(with = "serde_utils::bytes_4_hex")] + gloas_fork_version: [u8; 4], + #[serde(default)] + #[serde(serialize_with = "serialize_fork_epoch")] + #[serde(deserialize_with = "deserialize_fork_epoch")] + pub gloas_fork_epoch: Option>, + #[serde(with = "serde_utils::quoted_u64")] seconds_per_slot: u64, #[serde(with = "serde_utils::quoted_u64")] @@ -1805,6 +1848,11 @@ fn default_fulu_fork_version() -> [u8; 4] { [0xff, 0xff, 0xff, 0xff] } +fn default_gloas_fork_version() -> [u8; 4] { + // This value shouldn't be used. + [0xff, 0xff, 0xff, 0xff] +} + /// Placeholder value: 2^256-2^10 (115792089237316195423570985008687907853269984665640564039457584007913129638912). /// /// Taken from https://github.com/ethereum/consensus-specs/blob/d5e4828aecafaf1c57ef67a5f23c4ae7b08c5137/configs/mainnet.yaml#L15-L16 @@ -2101,6 +2149,11 @@ impl Config { .fulu_fork_epoch .map(|epoch| MaybeQuoted { value: epoch }), + gloas_fork_version: spec.gloas_fork_version, + gloas_fork_epoch: spec + .gloas_fork_epoch + .map(|epoch| MaybeQuoted { value: epoch }), + seconds_per_slot: spec.seconds_per_slot, seconds_per_eth1_block: spec.seconds_per_eth1_block, min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay, @@ -2190,6 +2243,8 @@ impl Config { electra_fork_version, fulu_fork_epoch, fulu_fork_version, + gloas_fork_version, + gloas_fork_epoch, seconds_per_slot, seconds_per_eth1_block, min_validator_withdrawability_delay, @@ -2261,6 +2316,8 @@ impl Config { electra_fork_version, fulu_fork_epoch: fulu_fork_epoch.map(|q| q.value), fulu_fork_version, + gloas_fork_version, + gloas_fork_epoch: gloas_fork_epoch.map(|q| q.value), seconds_per_slot, seconds_per_eth1_block, min_validator_withdrawability_delay, @@ -2548,6 +2605,8 @@ mod yaml_tests { ELECTRA_FORK_EPOCH: 128 FULU_FORK_VERSION: 0x70355025 FULU_FORK_EPOCH: 256 + GLOAS_FORK_VERSION: 0x80355025 + GLOAS_FORK_EPOCH: 512 BLOB_SCHEDULE: - EPOCH: 512 MAX_BLOBS_PER_BLOCK: 12 diff --git a/consensus/types/src/config_and_preset.rs b/consensus/types/src/config_and_preset.rs index 2ad9fae504d..16b09c9c088 100644 --- a/consensus/types/src/config_and_preset.rs +++ b/consensus/types/src/config_and_preset.rs @@ -1,6 +1,6 @@ use crate::{ AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, ChainSpec, Config, DenebPreset, - ElectraPreset, EthSpec, FuluPreset, consts::altair, consts::deneb, + ElectraPreset, EthSpec, FuluPreset, GloasPreset, consts::altair, consts::deneb, }; use maplit::hashmap; use serde::{Deserialize, Serialize}; @@ -12,7 +12,7 @@ use superstruct::superstruct; /// /// Mostly useful for the API. #[superstruct( - variants(Deneb, Electra, Fulu), + variants(Deneb, Electra, Fulu, Gloas), variant_attributes(derive(Serialize, Deserialize, Debug, PartialEq, Clone)) )] #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] @@ -31,12 +31,15 @@ pub struct ConfigAndPreset { pub capella_preset: CapellaPreset, #[serde(flatten)] pub deneb_preset: DenebPreset, - #[superstruct(only(Electra, Fulu))] + #[superstruct(only(Electra, Fulu, Gloas))] #[serde(flatten)] pub electra_preset: ElectraPreset, - #[superstruct(only(Fulu))] + #[superstruct(only(Fulu, Gloas))] #[serde(flatten)] pub fulu_preset: FuluPreset, + #[superstruct(only(Gloas))] + #[serde(flatten)] + pub gloas_preset: GloasPreset, /// The `extra_fields` map allows us to gracefully decode fields intended for future hard forks. #[serde(flatten)] pub extra_fields: HashMap, @@ -52,7 +55,24 @@ impl ConfigAndPreset { let deneb_preset = DenebPreset::from_chain_spec::(spec); let extra_fields = get_extra_fields(spec); - if spec.is_fulu_scheduled() { + if spec.is_gloas_scheduled() { + let electra_preset = ElectraPreset::from_chain_spec::(spec); + let fulu_preset = FuluPreset::from_chain_spec::(spec); + let gloas_preset = GloasPreset::from_chain_spec::(spec); + + ConfigAndPreset::Gloas(ConfigAndPresetGloas { + config, + base_preset, + altair_preset, + bellatrix_preset, + capella_preset, + deneb_preset, + electra_preset, + fulu_preset, + gloas_preset, + extra_fields, + }) + } else if spec.is_fulu_scheduled() { let electra_preset = ElectraPreset::from_chain_spec::(spec); let fulu_preset = FuluPreset::from_chain_spec::(spec); @@ -139,8 +159,8 @@ mod test { .open(tmp_file.as_ref()) .expect("error opening file"); let mut mainnet_spec = ChainSpec::mainnet(); - // setting fulu_fork_epoch because we are roundtripping a fulu config - mainnet_spec.fulu_fork_epoch = Some(Epoch::new(42)); + // setting gloas_fork_epoch because we are roundtripping a gloas config + mainnet_spec.gloas_fork_epoch = Some(Epoch::new(42)); let mut yamlconfig = ConfigAndPreset::from_chain_spec::(&mainnet_spec); let (k1, v1) = ("SAMPLE_HARDFORK_KEY1", "123456789"); let (k2, v2) = ("SAMPLE_HARDFORK_KEY2", "987654321"); @@ -158,8 +178,8 @@ mod test { .write(false) .open(tmp_file.as_ref()) .expect("error while opening the file"); - let from: ConfigAndPresetFulu = + let from: ConfigAndPresetGloas = serde_yaml::from_reader(reader).expect("error while deserializing"); - assert_eq!(ConfigAndPreset::Fulu(from), yamlconfig); + assert_eq!(ConfigAndPreset::Gloas(from), yamlconfig); } } diff --git a/consensus/types/src/execution_payload.rs b/consensus/types/src/execution_payload.rs index be9b114eddd..7a899e5f022 100644 --- a/consensus/types/src/execution_payload.rs +++ b/consensus/types/src/execution_payload.rs @@ -15,7 +15,7 @@ pub type Transactions = VariableList< pub type Withdrawals = VariableList::MaxWithdrawalsPerPayload>; #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Default, @@ -88,12 +88,12 @@ pub struct ExecutionPayload { pub block_hash: ExecutionBlockHash, #[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")] pub transactions: Transactions, - #[superstruct(only(Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))] pub withdrawals: Withdrawals, - #[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] pub blob_gas_used: u64, - #[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] pub excess_blob_gas: u64, } @@ -122,6 +122,7 @@ impl ForkVersionDecode for ExecutionPayload { ForkName::Deneb => ExecutionPayloadDeneb::from_ssz_bytes(bytes).map(Self::Deneb), ForkName::Electra => ExecutionPayloadElectra::from_ssz_bytes(bytes).map(Self::Electra), ForkName::Fulu => ExecutionPayloadFulu::from_ssz_bytes(bytes).map(Self::Fulu), + ForkName::Gloas => ExecutionPayloadGloas::from_ssz_bytes(bytes).map(Self::Gloas), } } } @@ -169,6 +170,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayload ForkName::Fulu => { Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?) } + ForkName::Gloas => { + Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?) + } }) } } @@ -181,6 +185,7 @@ impl ExecutionPayload { ExecutionPayload::Deneb(_) => ForkName::Deneb, ExecutionPayload::Electra(_) => ForkName::Electra, ExecutionPayload::Fulu(_) => ForkName::Fulu, + ExecutionPayload::Gloas(_) => ForkName::Gloas, } } } diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index 6127d63046a..2f5fac87a9a 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -8,7 +8,7 @@ use tree_hash::TreeHash; use tree_hash_derive::TreeHash; #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Default, @@ -84,12 +84,12 @@ pub struct ExecutionPayloadHeader { pub block_hash: ExecutionBlockHash, #[superstruct(getter(copy))] pub transactions_root: Hash256, - #[superstruct(only(Capella, Deneb, Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas), partial_getter(copy))] pub withdrawals_root: Hash256, - #[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] pub blob_gas_used: u64, - #[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))] + #[superstruct(only(Deneb, Electra, Fulu, Gloas), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] pub excess_blob_gas: u64, } @@ -115,6 +115,7 @@ impl ExecutionPayloadHeader { ExecutionPayloadHeaderElectra::from_ssz_bytes(bytes).map(Self::Electra) } ForkName::Fulu => ExecutionPayloadHeaderFulu::from_ssz_bytes(bytes).map(Self::Fulu), + ForkName::Gloas => ExecutionPayloadHeaderGloas::from_ssz_bytes(bytes).map(Self::Gloas), } } @@ -136,6 +137,7 @@ impl ExecutionPayloadHeader { ExecutionPayloadHeader::Deneb(_) => ForkName::Deneb, ExecutionPayloadHeader::Electra(_) => ForkName::Electra, ExecutionPayloadHeader::Fulu(_) => ForkName::Fulu, + ExecutionPayloadHeader::Gloas(_) => ForkName::Gloas, } } } @@ -243,6 +245,30 @@ impl ExecutionPayloadHeaderElectra { } } +impl ExecutionPayloadHeaderFulu { + pub fn upgrade_to_gloas(&self) -> ExecutionPayloadHeaderGloas { + ExecutionPayloadHeaderGloas { + parent_hash: self.parent_hash, + fee_recipient: self.fee_recipient, + state_root: self.state_root, + receipts_root: self.receipts_root, + logs_bloom: self.logs_bloom.clone(), + prev_randao: self.prev_randao, + block_number: self.block_number, + gas_limit: self.gas_limit, + gas_used: self.gas_used, + timestamp: self.timestamp, + extra_data: self.extra_data.clone(), + base_fee_per_gas: self.base_fee_per_gas, + block_hash: self.block_hash, + transactions_root: self.transactions_root, + withdrawals_root: self.withdrawals_root, + blob_gas_used: self.blob_gas_used, + excess_blob_gas: self.excess_blob_gas, + } + } +} + impl<'a, E: EthSpec> From<&'a ExecutionPayloadBellatrix> for ExecutionPayloadHeaderBellatrix { fn from(payload: &'a ExecutionPayloadBellatrix) -> Self { Self { @@ -358,6 +384,30 @@ impl<'a, E: EthSpec> From<&'a ExecutionPayloadFulu> for ExecutionPayloadHeade } } +impl<'a, E: EthSpec> From<&'a ExecutionPayloadGloas> for ExecutionPayloadHeaderGloas { + fn from(payload: &'a ExecutionPayloadGloas) -> Self { + Self { + parent_hash: payload.parent_hash, + fee_recipient: payload.fee_recipient, + state_root: payload.state_root, + receipts_root: payload.receipts_root, + logs_bloom: payload.logs_bloom.clone(), + prev_randao: payload.prev_randao, + block_number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + extra_data: payload.extra_data.clone(), + base_fee_per_gas: payload.base_fee_per_gas, + block_hash: payload.block_hash, + transactions_root: payload.transactions.tree_hash_root(), + withdrawals_root: payload.withdrawals.tree_hash_root(), + blob_gas_used: payload.blob_gas_used, + excess_blob_gas: payload.excess_blob_gas, + } + } +} + // These impls are required to work around an inelegance in `to_execution_payload_header`. // They only clone headers so they should be relatively cheap. impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderBellatrix { @@ -390,6 +440,12 @@ impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderFulu { } } +impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderGloas { + fn from(payload: &'a Self) -> Self { + payload.clone() + } +} + impl<'a, E: EthSpec> From> for ExecutionPayloadHeader { fn from(payload: ExecutionPayloadRef<'a, E>) -> Self { map_execution_payload_ref_into_execution_payload_header!( @@ -451,6 +507,9 @@ impl ExecutionPayloadHeaderRefMut<'_, E> { ExecutionPayloadHeaderRefMut::Fulu(mut_ref) => { *mut_ref = header.try_into()?; } + ExecutionPayloadHeaderRefMut::Gloas(mut_ref) => { + *mut_ref = header.try_into()?; + } } Ok(()) } @@ -478,6 +537,16 @@ impl TryFrom> for ExecutionPayloadHeaderFu } } +impl TryFrom> for ExecutionPayloadHeaderGloas { + type Error = BeaconStateError; + fn try_from(header: ExecutionPayloadHeader) -> Result { + match header { + ExecutionPayloadHeader::Gloas(execution_payload_header) => Ok(execution_payload_header), + _ => Err(BeaconStateError::IncorrectStateVariant), + } + } +} + impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadHeader { fn context_deserialize(deserializer: D, context: ForkName) -> Result where @@ -511,6 +580,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadHead ForkName::Fulu => { Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?) } + ForkName::Gloas => { + Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?) + } }) } } diff --git a/consensus/types/src/fork_name.rs b/consensus/types/src/fork_name.rs index 4fc26ccffa8..f12b14ff6ed 100644 --- a/consensus/types/src/fork_name.rs +++ b/consensus/types/src/fork_name.rs @@ -18,6 +18,7 @@ pub enum ForkName { Deneb, Electra, Fulu, + Gloas, } impl ForkName { @@ -30,6 +31,7 @@ impl ForkName { ForkName::Deneb, ForkName::Electra, ForkName::Fulu, + ForkName::Gloas, ] } @@ -64,6 +66,7 @@ impl ForkName { spec.deneb_fork_epoch = None; spec.electra_fork_epoch = None; spec.fulu_fork_epoch = None; + spec.gloas_fork_epoch = None; spec } ForkName::Altair => { @@ -73,6 +76,7 @@ impl ForkName { spec.deneb_fork_epoch = None; spec.electra_fork_epoch = None; spec.fulu_fork_epoch = None; + spec.gloas_fork_epoch = None; spec } ForkName::Bellatrix => { @@ -82,6 +86,7 @@ impl ForkName { spec.deneb_fork_epoch = None; spec.electra_fork_epoch = None; spec.fulu_fork_epoch = None; + spec.gloas_fork_epoch = None; spec } ForkName::Capella => { @@ -91,6 +96,7 @@ impl ForkName { spec.deneb_fork_epoch = None; spec.electra_fork_epoch = None; spec.fulu_fork_epoch = None; + spec.gloas_fork_epoch = None; spec } ForkName::Deneb => { @@ -100,6 +106,7 @@ impl ForkName { spec.deneb_fork_epoch = Some(Epoch::new(0)); spec.electra_fork_epoch = None; spec.fulu_fork_epoch = None; + spec.gloas_fork_epoch = None; spec } ForkName::Electra => { @@ -109,6 +116,7 @@ impl ForkName { spec.deneb_fork_epoch = Some(Epoch::new(0)); spec.electra_fork_epoch = Some(Epoch::new(0)); spec.fulu_fork_epoch = None; + spec.gloas_fork_epoch = None; spec } ForkName::Fulu => { @@ -118,6 +126,17 @@ impl ForkName { spec.deneb_fork_epoch = Some(Epoch::new(0)); spec.electra_fork_epoch = Some(Epoch::new(0)); spec.fulu_fork_epoch = Some(Epoch::new(0)); + spec.gloas_fork_epoch = None; + spec + } + ForkName::Gloas => { + spec.altair_fork_epoch = Some(Epoch::new(0)); + spec.bellatrix_fork_epoch = Some(Epoch::new(0)); + spec.capella_fork_epoch = Some(Epoch::new(0)); + spec.deneb_fork_epoch = Some(Epoch::new(0)); + spec.electra_fork_epoch = Some(Epoch::new(0)); + spec.fulu_fork_epoch = Some(Epoch::new(0)); + spec.gloas_fork_epoch = Some(Epoch::new(0)); spec } } @@ -135,6 +154,7 @@ impl ForkName { ForkName::Deneb => Some(ForkName::Capella), ForkName::Electra => Some(ForkName::Deneb), ForkName::Fulu => Some(ForkName::Electra), + ForkName::Gloas => Some(ForkName::Fulu), } } @@ -149,7 +169,8 @@ impl ForkName { ForkName::Capella => Some(ForkName::Deneb), ForkName::Deneb => Some(ForkName::Electra), ForkName::Electra => Some(ForkName::Fulu), - ForkName::Fulu => None, + ForkName::Fulu => Some(ForkName::Gloas), + ForkName::Gloas => None, } } @@ -176,6 +197,10 @@ impl ForkName { pub fn fulu_enabled(self) -> bool { self >= ForkName::Fulu } + + pub fn gloas_enabled(self) -> bool { + self >= ForkName::Gloas + } } /// Map a fork name into a fork-versioned superstruct type like `BeaconBlock`. @@ -231,6 +256,10 @@ macro_rules! map_fork_name_with { let (value, extra_data) = $body; ($t::Fulu(value), extra_data) } + ForkName::Gloas => { + let (value, extra_data) = $body; + ($t::Gloas(value), extra_data) + } } }; } @@ -247,6 +276,7 @@ impl FromStr for ForkName { "deneb" => ForkName::Deneb, "electra" => ForkName::Electra, "fulu" => ForkName::Fulu, + "gloas" => ForkName::Gloas, _ => return Err(format!("unknown fork name: {}", fork_name)), }) } @@ -262,6 +292,7 @@ impl Display for ForkName { ForkName::Deneb => "deneb".fmt(f), ForkName::Electra => "electra".fmt(f), ForkName::Fulu => "fulu".fmt(f), + ForkName::Gloas => "gloas".fmt(f), } } } diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 0d56f01188e..8e83fed1d9a 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -128,13 +128,13 @@ pub use crate::attester_slashing::{ }; pub use crate::beacon_block::{ BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockBellatrix, BeaconBlockCapella, - BeaconBlockDeneb, BeaconBlockElectra, BeaconBlockFulu, BeaconBlockRef, BeaconBlockRefMut, - BlindedBeaconBlock, BlockImportSource, EmptyBlock, + BeaconBlockDeneb, BeaconBlockElectra, BeaconBlockFulu, BeaconBlockGloas, BeaconBlockRef, + BeaconBlockRefMut, BlindedBeaconBlock, BlockImportSource, EmptyBlock, }; pub use crate::beacon_block_body::{ BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyBellatrix, BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu, - BeaconBlockBodyRef, BeaconBlockBodyRefMut, + BeaconBlockBodyGloas, BeaconBlockBodyRef, BeaconBlockBodyRefMut, }; pub use crate::beacon_block_header::BeaconBlockHeader; pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee}; @@ -148,6 +148,7 @@ pub use crate::chain_spec::{ChainSpec, Config, Domain}; pub use crate::checkpoint::Checkpoint; pub use crate::config_and_preset::{ ConfigAndPreset, ConfigAndPresetDeneb, ConfigAndPresetElectra, ConfigAndPresetFulu, + ConfigAndPresetGloas, }; pub use crate::consolidation_request::ConsolidationRequest; pub use crate::contribution_and_proof::ContributionAndProof; @@ -168,13 +169,13 @@ pub use crate::execution_block_hash::ExecutionBlockHash; pub use crate::execution_block_header::{EncodableExecutionBlockHeader, ExecutionBlockHeader}; pub use crate::execution_payload::{ ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, - ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadRef, Transaction, Transactions, - Withdrawals, + ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionPayloadRef, + Transaction, Transactions, Withdrawals, }; pub use crate::execution_payload_header::{ ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, - ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, + ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, }; pub use crate::execution_requests::{ExecutionRequests, RequestType}; pub use crate::fork::Fork; @@ -189,32 +190,35 @@ pub use crate::indexed_attestation::{ pub use crate::light_client_bootstrap::{ LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella, LightClientBootstrapDeneb, LightClientBootstrapElectra, LightClientBootstrapFulu, + LightClientBootstrapGloas, }; pub use crate::light_client_finality_update::{ LightClientFinalityUpdate, LightClientFinalityUpdateAltair, LightClientFinalityUpdateCapella, LightClientFinalityUpdateDeneb, LightClientFinalityUpdateElectra, - LightClientFinalityUpdateFulu, + LightClientFinalityUpdateFulu, LightClientFinalityUpdateGloas, }; pub use crate::light_client_header::{ LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, - LightClientHeaderElectra, LightClientHeaderFulu, + LightClientHeaderElectra, LightClientHeaderFulu, LightClientHeaderGloas, }; pub use crate::light_client_optimistic_update::{ LightClientOptimisticUpdate, LightClientOptimisticUpdateAltair, LightClientOptimisticUpdateCapella, LightClientOptimisticUpdateDeneb, LightClientOptimisticUpdateElectra, LightClientOptimisticUpdateFulu, + LightClientOptimisticUpdateGloas, }; pub use crate::light_client_update::{ Error as LightClientUpdateError, LightClientUpdate, LightClientUpdateAltair, LightClientUpdateCapella, LightClientUpdateDeneb, LightClientUpdateElectra, - LightClientUpdateFulu, MerkleProof, + LightClientUpdateFulu, LightClientUpdateGloas, MerkleProof, }; pub use crate::participation_flags::ParticipationFlags; pub use crate::payload::{ AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella, - BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadRef, BlockType, - ExecPayload, FullPayload, FullPayloadBellatrix, FullPayloadCapella, FullPayloadDeneb, - FullPayloadElectra, FullPayloadFulu, FullPayloadRef, OwnedExecPayload, + BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadGloas, + BlindedPayloadRef, BlockType, ExecPayload, FullPayload, FullPayloadBellatrix, + FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu, FullPayloadGloas, + FullPayloadRef, OwnedExecPayload, }; pub use crate::pending_attestation::PendingAttestation; pub use crate::pending_consolidation::PendingConsolidation; @@ -222,7 +226,7 @@ pub use crate::pending_deposit::PendingDeposit; pub use crate::pending_partial_withdrawal::PendingPartialWithdrawal; pub use crate::preset::{ AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, DenebPreset, ElectraPreset, - FuluPreset, + FuluPreset, GloasPreset, }; pub use crate::proposer_preparation_data::ProposerPreparationData; pub use crate::proposer_slashing::ProposerSlashing; @@ -237,7 +241,7 @@ pub use crate::signed_aggregate_and_proof::{ pub use crate::signed_beacon_block::{ SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix, SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, - SignedBeaconBlockFulu, SignedBeaconBlockHash, SignedBlindedBeaconBlock, + SignedBeaconBlockFulu, SignedBeaconBlockGloas, SignedBeaconBlockHash, SignedBlindedBeaconBlock, ssz_tagged_signed_beacon_block, ssz_tagged_signed_beacon_block_arc, }; pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader; diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client_bootstrap.rs index 88558254e80..5850db876c2 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client_bootstrap.rs @@ -2,8 +2,8 @@ use crate::context_deserialize; use crate::{ BeaconState, ChainSpec, ContextDeserialize, EthSpec, FixedVector, ForkName, Hash256, LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, - LightClientHeaderElectra, LightClientHeaderFulu, SignedBlindedBeaconBlock, Slot, SyncCommittee, - light_client_update::*, test_utils::TestRandom, + LightClientHeaderElectra, LightClientHeaderFulu, LightClientHeaderGloas, + SignedBlindedBeaconBlock, Slot, SyncCommittee, light_client_update::*, test_utils::TestRandom, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -17,7 +17,7 @@ use tree_hash_derive::TreeHash; /// A LightClientBootstrap is the initializer we send over to light_client nodes /// that are trying to generate their basic storage when booting up. #[superstruct( - variants(Altair, Capella, Deneb, Electra, Fulu), + variants(Altair, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -62,6 +62,8 @@ pub struct LightClientBootstrap { pub header: LightClientHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "header_fulu"))] pub header: LightClientHeaderFulu, + #[superstruct(only(Gloas), partial_getter(rename = "header_gloas"))] + pub header: LightClientHeaderGloas, /// The `SyncCommittee` used in the requested period. pub current_sync_committee: Arc>, /// Merkle proof for sync committee @@ -71,7 +73,7 @@ pub struct LightClientBootstrap { )] pub current_sync_committee_branch: FixedVector, #[superstruct( - only(Electra, Fulu), + only(Electra, Fulu, Gloas), partial_getter(rename = "current_sync_committee_branch_electra") )] pub current_sync_committee_branch: FixedVector, @@ -88,6 +90,7 @@ impl LightClientBootstrap { Self::Deneb(_) => func(ForkName::Deneb), Self::Electra(_) => func(ForkName::Electra), Self::Fulu(_) => func(ForkName::Fulu), + Self::Gloas(_) => func(ForkName::Gloas), } } @@ -107,6 +110,7 @@ impl LightClientBootstrap { ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb::from_ssz_bytes(bytes)?), ForkName::Electra => Self::Electra(LightClientBootstrapElectra::from_ssz_bytes(bytes)?), ForkName::Fulu => Self::Fulu(LightClientBootstrapFulu::from_ssz_bytes(bytes)?), + ForkName::Gloas => Self::Gloas(LightClientBootstrapGloas::from_ssz_bytes(bytes)?), ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientBootstrap decoding for {fork_name} not implemented" @@ -128,6 +132,7 @@ impl LightClientBootstrap { ForkName::Deneb => as Encode>::ssz_fixed_len(), ForkName::Electra => as Encode>::ssz_fixed_len(), ForkName::Fulu => as Encode>::ssz_fixed_len(), + ForkName::Gloas => as Encode>::ssz_fixed_len(), }; fixed_len + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } @@ -168,6 +173,11 @@ impl LightClientBootstrap { current_sync_committee, current_sync_committee_branch: current_sync_committee_branch.into(), }), + ForkName::Gloas => Self::Gloas(LightClientBootstrapGloas { + header: LightClientHeaderGloas::block_to_light_client_header(block)?, + current_sync_committee, + current_sync_committee_branch: current_sync_committee_branch.into(), + }), }; Ok(light_client_bootstrap) @@ -213,6 +223,11 @@ impl LightClientBootstrap { current_sync_committee, current_sync_committee_branch: current_sync_committee_branch.into(), }), + ForkName::Gloas => Self::Gloas(LightClientBootstrapGloas { + header: LightClientHeaderGloas::block_to_light_client_header(block)?, + current_sync_committee, + current_sync_committee_branch: current_sync_committee_branch.into(), + }), }; Ok(light_client_bootstrap) @@ -252,6 +267,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientBootstrap ForkName::Fulu => { Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?) } + ForkName::Gloas => { + Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?) + } }) } } @@ -288,4 +306,10 @@ mod tests { use crate::{LightClientBootstrapFulu, MainnetEthSpec}; ssz_tests!(LightClientBootstrapFulu); } + + #[cfg(test)] + mod gloas { + use crate::{LightClientBootstrapGloas, MainnetEthSpec}; + ssz_tests!(LightClientBootstrapGloas); + } } diff --git a/consensus/types/src/light_client_finality_update.rs b/consensus/types/src/light_client_finality_update.rs index df5954d4968..4fa98de40be 100644 --- a/consensus/types/src/light_client_finality_update.rs +++ b/consensus/types/src/light_client_finality_update.rs @@ -4,7 +4,8 @@ use crate::context_deserialize; use crate::{ ContextDeserialize, ForkName, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, LightClientHeaderElectra, LightClientHeaderFulu, - SignedBlindedBeaconBlock, light_client_update::*, test_utils::TestRandom, + LightClientHeaderGloas, SignedBlindedBeaconBlock, light_client_update::*, + test_utils::TestRandom, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -16,7 +17,7 @@ use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; #[superstruct( - variants(Altair, Capella, Deneb, Electra, Fulu), + variants(Altair, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -61,6 +62,8 @@ pub struct LightClientFinalityUpdate { pub attested_header: LightClientHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "attested_header_fulu"))] pub attested_header: LightClientHeaderFulu, + #[superstruct(only(Gloas), partial_getter(rename = "attested_header_gloas"))] + pub attested_header: LightClientHeaderGloas, /// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch). #[superstruct(only(Altair), partial_getter(rename = "finalized_header_altair"))] pub finalized_header: LightClientHeaderAltair, @@ -72,6 +75,8 @@ pub struct LightClientFinalityUpdate { pub finalized_header: LightClientHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "finalized_header_fulu"))] pub finalized_header: LightClientHeaderFulu, + #[superstruct(only(Gloas), partial_getter(rename = "finalized_header_gloas"))] + pub finalized_header: LightClientHeaderGloas, /// Merkle proof attesting finalized header. #[superstruct( only(Altair, Capella, Deneb), @@ -79,7 +84,7 @@ pub struct LightClientFinalityUpdate { )] pub finality_branch: FixedVector, #[superstruct( - only(Electra, Fulu), + only(Electra, Fulu, Gloas), partial_getter(rename = "finality_branch_electra") )] pub finality_branch: FixedVector, @@ -160,6 +165,17 @@ impl LightClientFinalityUpdate { sync_aggregate, signature_slot, }), + ForkName::Gloas => Self::Gloas(LightClientFinalityUpdateGloas { + attested_header: LightClientHeaderGloas::block_to_light_client_header( + attested_block, + )?, + finalized_header: LightClientHeaderGloas::block_to_light_client_header( + finalized_block, + )?, + finality_branch: finality_branch.into(), + sync_aggregate, + signature_slot, + }), ForkName::Base => return Err(Error::AltairForkNotActive), }; @@ -177,6 +193,7 @@ impl LightClientFinalityUpdate { Self::Deneb(_) => func(ForkName::Deneb), Self::Electra(_) => func(ForkName::Electra), Self::Fulu(_) => func(ForkName::Fulu), + Self::Gloas(_) => func(ForkName::Gloas), } } @@ -214,6 +231,7 @@ impl LightClientFinalityUpdate { Self::Electra(LightClientFinalityUpdateElectra::from_ssz_bytes(bytes)?) } ForkName::Fulu => Self::Fulu(LightClientFinalityUpdateFulu::from_ssz_bytes(bytes)?), + ForkName::Gloas => Self::Gloas(LightClientFinalityUpdateGloas::from_ssz_bytes(bytes)?), ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientFinalityUpdate decoding for {fork_name} not implemented" @@ -235,6 +253,7 @@ impl LightClientFinalityUpdate { ForkName::Deneb => as Encode>::ssz_fixed_len(), ForkName::Electra => as Encode>::ssz_fixed_len(), ForkName::Fulu => as Encode>::ssz_fixed_len(), + ForkName::Gloas => as Encode>::ssz_fixed_len(), }; // `2 *` because there are two headers in the update fixed_size + 2 * LightClientHeader::::ssz_max_var_len_for_fork(fork_name) @@ -287,6 +306,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientFinalityU ForkName::Fulu => { Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?) } + ForkName::Gloas => { + Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?) + } }) } } @@ -323,4 +345,10 @@ mod tests { use crate::{LightClientFinalityUpdateFulu, MainnetEthSpec}; ssz_tests!(LightClientFinalityUpdateFulu); } + + #[cfg(test)] + mod gloas { + use crate::{LightClientFinalityUpdateGloas, MainnetEthSpec}; + ssz_tests!(LightClientFinalityUpdateGloas); + } } diff --git a/consensus/types/src/light_client_header.rs b/consensus/types/src/light_client_header.rs index 676c1642e63..162203138ab 100644 --- a/consensus/types/src/light_client_header.rs +++ b/consensus/types/src/light_client_header.rs @@ -5,8 +5,8 @@ use crate::{BeaconBlockHeader, ExecutionPayloadHeader}; use crate::{ContextDeserialize, ForkName}; use crate::{ EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, FixedVector, Hash256, - SignedBlindedBeaconBlock, test_utils::TestRandom, + ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderGloas, + FixedVector, Hash256, SignedBlindedBeaconBlock, test_utils::TestRandom, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -18,7 +18,7 @@ use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; #[superstruct( - variants(Altair, Capella, Deneb, Electra, Fulu), + variants(Altair, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -68,8 +68,10 @@ pub struct LightClientHeader { pub execution: ExecutionPayloadHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "execution_payload_header_fulu"))] pub execution: ExecutionPayloadHeaderFulu, + #[superstruct(only(Gloas), partial_getter(rename = "execution_payload_header_gloas"))] + pub execution: ExecutionPayloadHeaderGloas, - #[superstruct(only(Capella, Deneb, Electra, Fulu))] + #[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))] pub execution_branch: FixedVector, #[ssz(skip_serializing, skip_deserializing)] @@ -104,6 +106,9 @@ impl LightClientHeader { ForkName::Fulu => { LightClientHeader::Fulu(LightClientHeaderFulu::block_to_light_client_header(block)?) } + ForkName::Gloas => LightClientHeader::Gloas( + LightClientHeaderGloas::block_to_light_client_header(block)?, + ), }; Ok(header) } @@ -125,6 +130,9 @@ impl LightClientHeader { ForkName::Fulu => { LightClientHeader::Fulu(LightClientHeaderFulu::from_ssz_bytes(bytes)?) } + ForkName::Gloas => { + LightClientHeader::Gloas(LightClientHeaderGloas::from_ssz_bytes(bytes)?) + } ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientHeader decoding for {fork_name} not implemented" @@ -340,6 +348,48 @@ impl Default for LightClientHeaderFulu { } } +impl LightClientHeaderGloas { + pub fn block_to_light_client_header( + block: &SignedBlindedBeaconBlock, + ) -> Result { + let payload = block + .message() + .execution_payload()? + .execution_payload_gloas()?; + + let header = ExecutionPayloadHeaderGloas::from(payload); + let beacon_block_body = BeaconBlockBody::from( + block + .message() + .body_gloas() + .map_err(|_| Error::BeaconBlockBodyError)? + .to_owned(), + ); + + let execution_branch = beacon_block_body + .to_ref() + .block_body_merkle_proof(EXECUTION_PAYLOAD_INDEX)?; + + Ok(LightClientHeaderGloas { + beacon: block.message().block_header(), + execution: header, + execution_branch: FixedVector::new(execution_branch)?, + _phantom_data: PhantomData, + }) + } +} + +impl Default for LightClientHeaderGloas { + fn default() -> Self { + Self { + beacon: BeaconBlockHeader::empty(), + execution: ExecutionPayloadHeaderGloas::default(), + execution_branch: FixedVector::default(), + _phantom_data: PhantomData, + } + } +} + impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientHeader { fn context_deserialize(deserializer: D, context: ForkName) -> Result where @@ -373,6 +423,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientHeader ForkName::Fulu => { Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?) } + ForkName::Gloas => { + Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?) + } }) } } @@ -403,4 +456,16 @@ mod tests { use crate::{LightClientHeaderElectra, MainnetEthSpec}; ssz_tests!(LightClientHeaderElectra); } + + #[cfg(test)] + mod fulu { + use crate::{LightClientHeaderFulu, MainnetEthSpec}; + ssz_tests!(LightClientHeaderFulu); + } + + #[cfg(test)] + mod gloas { + use crate::{LightClientHeaderGloas, MainnetEthSpec}; + ssz_tests!(LightClientHeaderGloas); + } } diff --git a/consensus/types/src/light_client_optimistic_update.rs b/consensus/types/src/light_client_optimistic_update.rs index 48e5e46ffe1..7528322d567 100644 --- a/consensus/types/src/light_client_optimistic_update.rs +++ b/consensus/types/src/light_client_optimistic_update.rs @@ -3,8 +3,8 @@ use crate::context_deserialize; use crate::test_utils::TestRandom; use crate::{ ChainSpec, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, - LightClientHeaderElectra, LightClientHeaderFulu, SignedBlindedBeaconBlock, - light_client_update::*, + LightClientHeaderElectra, LightClientHeaderFulu, LightClientHeaderGloas, + SignedBlindedBeaconBlock, light_client_update::*, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -19,7 +19,7 @@ use tree_hash_derive::TreeHash; /// A LightClientOptimisticUpdate is the update we send on each slot, /// it is based off the current unfinalized epoch is verified only against BLS signature. #[superstruct( - variants(Altair, Capella, Deneb, Electra, Fulu), + variants(Altair, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -64,6 +64,8 @@ pub struct LightClientOptimisticUpdate { pub attested_header: LightClientHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "attested_header_fulu"))] pub attested_header: LightClientHeaderFulu, + #[superstruct(only(Gloas), partial_getter(rename = "attested_header_gloas"))] + pub attested_header: LightClientHeaderGloas, /// current sync aggregate pub sync_aggregate: SyncAggregate, /// Slot of the sync aggregated signature @@ -119,6 +121,13 @@ impl LightClientOptimisticUpdate { sync_aggregate, signature_slot, }), + ForkName::Gloas => Self::Gloas(LightClientOptimisticUpdateGloas { + attested_header: LightClientHeaderGloas::block_to_light_client_header( + attested_block, + )?, + sync_aggregate, + signature_slot, + }), ForkName::Base => return Err(Error::AltairForkNotActive), }; @@ -135,6 +144,7 @@ impl LightClientOptimisticUpdate { Self::Deneb(_) => func(ForkName::Deneb), Self::Electra(_) => func(ForkName::Electra), Self::Fulu(_) => func(ForkName::Fulu), + Self::Gloas(_) => func(ForkName::Gloas), } } @@ -174,6 +184,9 @@ impl LightClientOptimisticUpdate { Self::Electra(LightClientOptimisticUpdateElectra::from_ssz_bytes(bytes)?) } ForkName::Fulu => Self::Fulu(LightClientOptimisticUpdateFulu::from_ssz_bytes(bytes)?), + ForkName::Gloas => { + Self::Gloas(LightClientOptimisticUpdateGloas::from_ssz_bytes(bytes)?) + } ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientOptimisticUpdate decoding for {fork_name} not implemented" @@ -195,6 +208,7 @@ impl LightClientOptimisticUpdate { ForkName::Deneb => as Encode>::ssz_fixed_len(), ForkName::Electra => as Encode>::ssz_fixed_len(), ForkName::Fulu => as Encode>::ssz_fixed_len(), + ForkName::Gloas => as Encode>::ssz_fixed_len(), }; fixed_len + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } @@ -246,6 +260,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientOptimisti ForkName::Fulu => { Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?) } + ForkName::Gloas => { + Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?) + } }) } } @@ -282,4 +299,10 @@ mod tests { use crate::{LightClientOptimisticUpdateFulu, MainnetEthSpec}; ssz_tests!(LightClientOptimisticUpdateFulu); } + + #[cfg(test)] + mod gloas { + use crate::{LightClientOptimisticUpdateGloas, MainnetEthSpec}; + ssz_tests!(LightClientOptimisticUpdateGloas); + } } diff --git a/consensus/types/src/light_client_update.rs b/consensus/types/src/light_client_update.rs index 8a413f7e14c..bf1a8c614a7 100644 --- a/consensus/types/src/light_client_update.rs +++ b/consensus/types/src/light_client_update.rs @@ -5,7 +5,7 @@ use crate::light_client_header::LightClientHeaderElectra; use crate::{ ChainSpec, ContextDeserialize, Epoch, ForkName, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, LightClientHeaderFulu, - SignedBlindedBeaconBlock, beacon_state, test_utils::TestRandom, + LightClientHeaderGloas, SignedBlindedBeaconBlock, beacon_state, test_utils::TestRandom, }; use derivative::Derivative; use safe_arith::ArithError; @@ -100,7 +100,7 @@ impl From for Error { /// or to sync up to the last committee period, we need to have one ready for each ALTAIR period /// we go over, note: there is no need to keep all of the updates from [ALTAIR_PERIOD, CURRENT_PERIOD]. #[superstruct( - variants(Altair, Capella, Deneb, Electra, Fulu), + variants(Altair, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -145,6 +145,8 @@ pub struct LightClientUpdate { pub attested_header: LightClientHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "attested_header_fulu"))] pub attested_header: LightClientHeaderFulu, + #[superstruct(only(Gloas), partial_getter(rename = "attested_header_gloas"))] + pub attested_header: LightClientHeaderGloas, /// The `SyncCommittee` used in the next period. pub next_sync_committee: Arc>, // Merkle proof for next sync committee @@ -154,7 +156,7 @@ pub struct LightClientUpdate { )] pub next_sync_committee_branch: NextSyncCommitteeBranch, #[superstruct( - only(Electra, Fulu), + only(Electra, Fulu, Gloas), partial_getter(rename = "next_sync_committee_branch_electra") )] pub next_sync_committee_branch: NextSyncCommitteeBranchElectra, @@ -169,6 +171,8 @@ pub struct LightClientUpdate { pub finalized_header: LightClientHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "finalized_header_fulu"))] pub finalized_header: LightClientHeaderFulu, + #[superstruct(only(Gloas), partial_getter(rename = "finalized_header_gloas"))] + pub finalized_header: LightClientHeaderGloas, /// Merkle proof attesting finalized header. #[superstruct( only(Altair, Capella, Deneb), @@ -176,7 +180,7 @@ pub struct LightClientUpdate { )] pub finality_branch: FinalityBranch, #[superstruct( - only(Electra, Fulu), + only(Electra, Fulu, Gloas), partial_getter(rename = "finality_branch_electra") )] pub finality_branch: FinalityBranchElectra, @@ -216,6 +220,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientUpdate ForkName::Fulu => { Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?) } + ForkName::Gloas => { + Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?) + } }) } } @@ -356,6 +363,30 @@ impl LightClientUpdate { sync_aggregate: sync_aggregate.clone(), signature_slot: block_slot, }) + } + fork_name @ ForkName::Gloas => { + let attested_header = + LightClientHeaderGloas::block_to_light_client_header(attested_block)?; + + let finalized_header = if let Some(finalized_block) = finalized_block { + if finalized_block.fork_name_unchecked() == fork_name { + LightClientHeaderGloas::block_to_light_client_header(finalized_block)? + } else { + LightClientHeaderGloas::default() + } + } else { + LightClientHeaderGloas::default() + }; + + Self::Gloas(LightClientUpdateGloas { + attested_header, + next_sync_committee, + next_sync_committee_branch: next_sync_committee_branch.into(), + finalized_header, + finality_branch: finality_branch.into(), + sync_aggregate: sync_aggregate.clone(), + signature_slot: block_slot, + }) } // To add a new fork, just append the new fork variant on the latest fork. Forks that // have a distinct execution header will need a new LightClientUpdate variant only // if you need to test or support lightclient usages @@ -373,6 +404,7 @@ impl LightClientUpdate { ForkName::Deneb => Self::Deneb(LightClientUpdateDeneb::from_ssz_bytes(bytes)?), ForkName::Electra => Self::Electra(LightClientUpdateElectra::from_ssz_bytes(bytes)?), ForkName::Fulu => Self::Fulu(LightClientUpdateFulu::from_ssz_bytes(bytes)?), + ForkName::Gloas => Self::Gloas(LightClientUpdateGloas::from_ssz_bytes(bytes)?), ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientUpdate decoding for {fork_name} not implemented" @@ -390,6 +422,7 @@ impl LightClientUpdate { LightClientUpdate::Deneb(update) => update.attested_header.beacon.slot, LightClientUpdate::Electra(update) => update.attested_header.beacon.slot, LightClientUpdate::Fulu(update) => update.attested_header.beacon.slot, + LightClientUpdate::Gloas(update) => update.attested_header.beacon.slot, } } @@ -400,6 +433,7 @@ impl LightClientUpdate { LightClientUpdate::Deneb(update) => update.finalized_header.beacon.slot, LightClientUpdate::Electra(update) => update.finalized_header.beacon.slot, LightClientUpdate::Fulu(update) => update.finalized_header.beacon.slot, + LightClientUpdate::Gloas(update) => update.finalized_header.beacon.slot, } } @@ -520,6 +554,7 @@ impl LightClientUpdate { ForkName::Deneb => as Encode>::ssz_fixed_len(), ForkName::Electra => as Encode>::ssz_fixed_len(), ForkName::Fulu => as Encode>::ssz_fixed_len(), + ForkName::Gloas => as Encode>::ssz_fixed_len(), }; fixed_len + 2 * LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } @@ -534,6 +569,7 @@ impl LightClientUpdate { Self::Deneb(_) => func(ForkName::Deneb), Self::Electra(_) => func(ForkName::Electra), Self::Fulu(_) => func(ForkName::Fulu), + Self::Gloas(_) => func(ForkName::Gloas), } } } @@ -596,6 +632,13 @@ mod tests { ssz_tests!(LightClientUpdateFulu); } + #[cfg(test)] + mod gloas { + use super::*; + use crate::MainnetEthSpec; + ssz_tests!(LightClientUpdateGloas); + } + #[test] fn finalized_root_params() { assert!(2usize.pow(FINALIZED_ROOT_PROOF_LEN as u32) <= FINALIZED_ROOT_INDEX); diff --git a/consensus/types/src/payload.rs b/consensus/types/src/payload.rs index b2866ecfd1f..28dc10f9384 100644 --- a/consensus/types/src/payload.rs +++ b/consensus/types/src/payload.rs @@ -105,6 +105,7 @@ pub trait AbstractExecPayload: + TryInto + TryInto + TryInto + + TryInto + Sync { type Ref<'a>: ExecPayload @@ -113,7 +114,8 @@ pub trait AbstractExecPayload: + From<&'a Self::Capella> + From<&'a Self::Deneb> + From<&'a Self::Electra> - + From<&'a Self::Fulu>; + + From<&'a Self::Fulu> + + From<&'a Self::Gloas>; type Bellatrix: OwnedExecPayload + Into @@ -140,10 +142,15 @@ pub trait AbstractExecPayload: + for<'a> From>> + TryFrom> + Sync; + type Gloas: OwnedExecPayload + + Into + + for<'a> From>> + + TryFrom> + + Sync; } #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -198,6 +205,8 @@ pub struct FullPayload { pub execution_payload: ExecutionPayloadElectra, #[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))] pub execution_payload: ExecutionPayloadFulu, + #[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))] + pub execution_payload: ExecutionPayloadGloas, } impl From> for ExecutionPayload { @@ -309,6 +318,7 @@ impl ExecPayload for FullPayload { FullPayload::Deneb(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()), FullPayload::Electra(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()), FullPayload::Fulu(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()), + FullPayload::Gloas(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()), } } @@ -320,6 +330,7 @@ impl ExecPayload for FullPayload { FullPayload::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used), FullPayload::Electra(inner) => Ok(inner.execution_payload.blob_gas_used), FullPayload::Fulu(inner) => Ok(inner.execution_payload.blob_gas_used), + FullPayload::Gloas(inner) => Ok(inner.execution_payload.blob_gas_used), } } @@ -351,6 +362,7 @@ impl FullPayload { ForkName::Deneb => Ok(FullPayloadDeneb::default().into()), ForkName::Electra => Ok(FullPayloadElectra::default().into()), ForkName::Fulu => Ok(FullPayloadFulu::default().into()), + ForkName::Gloas => Ok(FullPayloadGloas::default().into()), } } } @@ -451,6 +463,9 @@ impl ExecPayload for FullPayloadRef<'_, E> { Ok(inner.execution_payload.withdrawals.tree_hash_root()) } FullPayloadRef::Fulu(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()), + FullPayloadRef::Gloas(inner) => { + Ok(inner.execution_payload.withdrawals.tree_hash_root()) + } } } @@ -462,6 +477,7 @@ impl ExecPayload for FullPayloadRef<'_, E> { FullPayloadRef::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used), FullPayloadRef::Electra(inner) => Ok(inner.execution_payload.blob_gas_used), FullPayloadRef::Fulu(inner) => Ok(inner.execution_payload.blob_gas_used), + FullPayloadRef::Gloas(inner) => Ok(inner.execution_payload.blob_gas_used), } } @@ -485,6 +501,7 @@ impl AbstractExecPayload for FullPayload { type Deneb = FullPayloadDeneb; type Electra = FullPayloadElectra; type Fulu = FullPayloadFulu; + type Gloas = FullPayloadGloas; } impl From> for FullPayload { @@ -503,7 +520,7 @@ impl TryFrom> for FullPayload { } #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -557,6 +574,8 @@ pub struct BlindedPayload { pub execution_payload_header: ExecutionPayloadHeaderElectra, #[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))] pub execution_payload_header: ExecutionPayloadHeaderFulu, + #[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))] + pub execution_payload_header: ExecutionPayloadHeaderGloas, } impl<'a, E: EthSpec> From> for BlindedPayload { @@ -646,6 +665,7 @@ impl ExecPayload for BlindedPayload { BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.withdrawals_root), BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.withdrawals_root), BlindedPayload::Fulu(inner) => Ok(inner.execution_payload_header.withdrawals_root), + BlindedPayload::Gloas(inner) => Ok(inner.execution_payload_header.withdrawals_root), } } @@ -657,6 +677,7 @@ impl ExecPayload for BlindedPayload { BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used), BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used), BlindedPayload::Fulu(inner) => Ok(inner.execution_payload_header.blob_gas_used), + BlindedPayload::Gloas(inner) => Ok(inner.execution_payload_header.blob_gas_used), } } @@ -756,6 +777,7 @@ impl<'b, E: EthSpec> ExecPayload for BlindedPayloadRef<'b, E> { Ok(inner.execution_payload_header.withdrawals_root) } BlindedPayloadRef::Fulu(inner) => Ok(inner.execution_payload_header.withdrawals_root), + BlindedPayloadRef::Gloas(inner) => Ok(inner.execution_payload_header.withdrawals_root), } } @@ -767,6 +789,7 @@ impl<'b, E: EthSpec> ExecPayload for BlindedPayloadRef<'b, E> { BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used), BlindedPayloadRef::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used), BlindedPayloadRef::Fulu(inner) => Ok(inner.execution_payload_header.blob_gas_used), + BlindedPayloadRef::Gloas(inner) => Ok(inner.execution_payload_header.blob_gas_used), } } @@ -1077,6 +1100,13 @@ impl_exec_payload_for_fork!( ExecutionPayloadFulu, Fulu ); +impl_exec_payload_for_fork!( + BlindedPayloadGloas, + FullPayloadGloas, + ExecutionPayloadHeaderGloas, + ExecutionPayloadGloas, + Gloas +); impl AbstractExecPayload for BlindedPayload { type Ref<'a> = BlindedPayloadRef<'a, E>; @@ -1085,6 +1115,7 @@ impl AbstractExecPayload for BlindedPayload { type Deneb = BlindedPayloadDeneb; type Electra = BlindedPayloadElectra; type Fulu = BlindedPayloadFulu; + type Gloas = BlindedPayloadGloas; } impl From> for BlindedPayload { @@ -1126,6 +1157,11 @@ impl From> for BlindedPayload { execution_payload_header, }) } + ExecutionPayloadHeader::Gloas(execution_payload_header) => { + Self::Gloas(BlindedPayloadGloas { + execution_payload_header, + }) + } } } } @@ -1148,6 +1184,9 @@ impl From> for ExecutionPayloadHeader { BlindedPayload::Fulu(blinded_payload) => { ExecutionPayloadHeader::Fulu(blinded_payload.execution_payload_header) } + BlindedPayload::Gloas(blinded_payload) => { + ExecutionPayloadHeader::Gloas(blinded_payload.execution_payload_header) + } } } } diff --git a/consensus/types/src/preset.rs b/consensus/types/src/preset.rs index b776cf58732..c31183192f2 100644 --- a/consensus/types/src/preset.rs +++ b/consensus/types/src/preset.rs @@ -316,6 +316,16 @@ impl FuluPreset { } } +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub struct GloasPreset {} + +impl GloasPreset { + pub fn from_chain_spec(_spec: &ChainSpec) -> Self { + Self {} + } +} + #[cfg(test)] mod test { use super::*; @@ -363,6 +373,9 @@ mod test { let fulu: FuluPreset = preset_from_file(&preset_name, "fulu.yaml"); assert_eq!(fulu, FuluPreset::from_chain_spec::(&spec)); + + let gloas: GloasPreset = preset_from_file(&preset_name, "gloas.yaml"); + assert_eq!(gloas, GloasPreset::from_chain_spec::(&spec)); } #[test] diff --git a/consensus/types/src/signed_beacon_block.rs b/consensus/types/src/signed_beacon_block.rs index a2a52e17c6a..979b91e30d5 100644 --- a/consensus/types/src/signed_beacon_block.rs +++ b/consensus/types/src/signed_beacon_block.rs @@ -41,7 +41,7 @@ impl From for Hash256 { /// A `BeaconBlock` and a signature from its proposer. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( derive( Debug, @@ -92,6 +92,8 @@ pub struct SignedBeaconBlock = FullP pub message: BeaconBlockElectra, #[superstruct(only(Fulu), partial_getter(rename = "message_fulu"))] pub message: BeaconBlockFulu, + #[superstruct(only(Gloas), partial_getter(rename = "message_gloas"))] + pub message: BeaconBlockGloas, pub signature: Signature, } @@ -178,6 +180,9 @@ impl> SignedBeaconBlock BeaconBlock::Fulu(message) => { SignedBeaconBlock::Fulu(SignedBeaconBlockFulu { message, signature }) } + BeaconBlock::Gloas(message) => { + SignedBeaconBlock::Gloas(SignedBeaconBlockGloas { message, signature }) + } } } @@ -643,6 +648,64 @@ impl SignedBeaconBlockFulu> { } } +impl SignedBeaconBlockGloas> { + pub fn into_full_block( + self, + execution_payload: ExecutionPayloadGloas, + ) -> SignedBeaconBlockGloas> { + let SignedBeaconBlockGloas { + message: + BeaconBlockGloas { + slot, + proposer_index, + parent_root, + state_root, + body: + BeaconBlockBodyGloas { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: BlindedPayloadGloas { .. }, + bls_to_execution_changes, + blob_kzg_commitments, + execution_requests, + }, + }, + signature, + } = self; + SignedBeaconBlockGloas { + message: BeaconBlockGloas { + slot, + proposer_index, + parent_root, + state_root, + body: BeaconBlockBodyGloas { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadGloas { execution_payload }, + bls_to_execution_changes, + blob_kzg_commitments, + execution_requests, + }, + }, + signature, + } + } +} + impl SignedBeaconBlock> { pub fn try_into_full_block( self, @@ -666,6 +729,9 @@ impl SignedBeaconBlock> { (SignedBeaconBlock::Fulu(block), Some(ExecutionPayload::Fulu(payload))) => { SignedBeaconBlock::Fulu(block.into_full_block(payload)) } + (SignedBeaconBlock::Gloas(block), Some(ExecutionPayload::Gloas(payload))) => { + SignedBeaconBlock::Gloas(block.into_full_block(payload)) + } // avoid wildcard matching forks so that compiler will // direct us here when a new fork has been added (SignedBeaconBlock::Bellatrix(_), _) => return None, @@ -673,6 +739,7 @@ impl SignedBeaconBlock> { (SignedBeaconBlock::Deneb(_), _) => return None, (SignedBeaconBlock::Electra(_), _) => return None, (SignedBeaconBlock::Fulu(_), _) => return None, + (SignedBeaconBlock::Gloas(_), _) => return None, }; Some(full_block) } @@ -818,6 +885,9 @@ pub mod ssz_tagged_signed_beacon_block { ForkName::Fulu => Ok(SignedBeaconBlock::Fulu( SignedBeaconBlockFulu::from_ssz_bytes(body)?, )), + ForkName::Gloas => Ok(SignedBeaconBlock::Gloas( + SignedBeaconBlockGloas::from_ssz_bytes(body)?, + )), } } } @@ -897,6 +967,7 @@ mod test { chain_spec.deneb_fork_epoch = Some(Epoch::new(4)); chain_spec.electra_fork_epoch = Some(Epoch::new(5)); chain_spec.fulu_fork_epoch = Some(Epoch::new(6)); + chain_spec.gloas_fork_epoch = Some(Epoch::new(7)); // check that we have all forks covered assert!(chain_spec.fork_epoch(ForkName::latest()).is_some()); @@ -934,7 +1005,11 @@ mod test { BeaconBlock::Electra(BeaconBlockElectra::empty(spec)), sig.clone(), ), - SignedBeaconBlock::from_block(BeaconBlock::Fulu(BeaconBlockFulu::empty(spec)), sig), + SignedBeaconBlock::from_block( + BeaconBlock::Fulu(BeaconBlockFulu::empty(spec)), + sig.clone(), + ), + SignedBeaconBlock::from_block(BeaconBlock::Gloas(BeaconBlockGloas::empty(spec)), sig), ]; for block in blocks { diff --git a/lcli/src/mock_el.rs b/lcli/src/mock_el.rs index 553cf505d74..ee6485b2388 100644 --- a/lcli/src/mock_el.rs +++ b/lcli/src/mock_el.rs @@ -21,6 +21,7 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< let cancun_time = parse_optional(matches, "cancun-time")?; let prague_time = parse_optional(matches, "prague-time")?; let osaka_time = parse_optional(matches, "osaka-time")?; + let amsterdam_time = parse_optional(matches, "amsterdam-time")?; let handle = env.core_context().executor.handle().unwrap(); let spec = Arc::new(E::default_spec()); @@ -40,6 +41,7 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< cancun_time, prague_time, osaka_time, + amsterdam_time, }; let kzg = None; let server: MockServer = MockServer::new_with_config(&handle, config, spec, kzg); diff --git a/testing/ef_tests/src/cases/fork.rs b/testing/ef_tests/src/cases/fork.rs index cae4fcf4054..78d802c2283 100644 --- a/testing/ef_tests/src/cases/fork.rs +++ b/testing/ef_tests/src/cases/fork.rs @@ -4,7 +4,7 @@ use crate::decode::{ssz_decode_state, yaml_decode_file}; use serde::Deserialize; use state_processing::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, - upgrade_to_electra, upgrade_to_fulu, + upgrade_to_electra, upgrade_to_fulu, upgrade_to_gloas, }; use types::BeaconState; @@ -72,6 +72,7 @@ impl Case for ForkTest { ForkName::Deneb => upgrade_to_deneb(&mut result_state, spec).map(|_| result_state), ForkName::Electra => upgrade_to_electra(&mut result_state, spec).map(|_| result_state), ForkName::Fulu => upgrade_to_fulu(&mut result_state, spec).map(|_| result_state), + ForkName::Gloas => upgrade_to_gloas(&mut result_state, spec).map(|_| result_state), }; compare_beacon_state_results_without_caches(&mut result, &mut expected) diff --git a/testing/ef_tests/src/cases/merkle_proof_validity.rs b/testing/ef_tests/src/cases/merkle_proof_validity.rs index bf77f5da4c3..1103d2fe822 100644 --- a/testing/ef_tests/src/cases/merkle_proof_validity.rs +++ b/testing/ef_tests/src/cases/merkle_proof_validity.rs @@ -4,7 +4,8 @@ use serde::Deserialize; use tree_hash::Hash256; use types::{ BeaconBlockBody, BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, - BeaconBlockBodyFulu, BeaconState, FixedVector, FullPayload, Unsigned, light_client_update, + BeaconBlockBodyFulu, BeaconBlockBodyGloas, BeaconState, FixedVector, FullPayload, Unsigned, + light_client_update, }; #[derive(Debug, Clone, Deserialize)] @@ -172,6 +173,9 @@ impl LoadCase for KzgInclusionMerkleProofValidity { ForkName::Fulu => { ssz_decode_file::>(&path.join("object.ssz_snappy"))?.into() } + ForkName::Gloas => { + ssz_decode_file::>(&path.join("object.ssz_snappy"))?.into() + } }; let merkle_proof = yaml_decode_file(&path.join("proof.yaml"))?; // Metadata does not exist in these tests but it is left like this just in case. @@ -290,6 +294,9 @@ impl LoadCase for BeaconBlockBodyMerkleProofValidity { ForkName::Fulu => { ssz_decode_file::>(&path.join("object.ssz_snappy"))?.into() } + ForkName::Gloas => { + ssz_decode_file::>(&path.join("object.ssz_snappy"))?.into() + } }; let merkle_proof = yaml_decode_file(&path.join("proof.yaml"))?; // Metadata does not exist in these tests but it is left like this just in case. diff --git a/testing/ef_tests/src/cases/transition.rs b/testing/ef_tests/src/cases/transition.rs index 9fdfdbf60c5..06aa8136506 100644 --- a/testing/ef_tests/src/cases/transition.rs +++ b/testing/ef_tests/src/cases/transition.rs @@ -68,6 +68,15 @@ impl LoadCase for TransitionTest { spec.electra_fork_epoch = Some(Epoch::new(0)); spec.fulu_fork_epoch = Some(metadata.fork_epoch); } + ForkName::Gloas => { + spec.altair_fork_epoch = Some(Epoch::new(0)); + spec.bellatrix_fork_epoch = Some(Epoch::new(0)); + spec.capella_fork_epoch = Some(Epoch::new(0)); + spec.deneb_fork_epoch = Some(Epoch::new(0)); + spec.electra_fork_epoch = Some(Epoch::new(0)); + spec.fulu_fork_epoch = Some(Epoch::new(0)); + spec.gloas_fork_epoch = Some(metadata.fork_epoch); + } } // Load blocks diff --git a/testing/ef_tests/src/handler.rs b/testing/ef_tests/src/handler.rs index 0fd2f5a0b2d..c31a75c3352 100644 --- a/testing/ef_tests/src/handler.rs +++ b/testing/ef_tests/src/handler.rs @@ -22,7 +22,7 @@ pub trait Handler { // Add forks here to exclude them from EF spec testing. Helpful for adding future or // unspecified forks. fn disabled_forks(&self) -> Vec { - vec![] + vec![ForkName::Gloas] } fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { diff --git a/testing/simulator/src/basic_sim.rs b/testing/simulator/src/basic_sim.rs index b47832ece76..23ec70ae5d8 100644 --- a/testing/simulator/src/basic_sim.rs +++ b/testing/simulator/src/basic_sim.rs @@ -32,6 +32,8 @@ const BELLATRIX_FORK_EPOCH: u64 = 0; const CAPELLA_FORK_EPOCH: u64 = 0; const DENEB_FORK_EPOCH: u64 = 0; const ELECTRA_FORK_EPOCH: u64 = 2; +// const FULU_FORK_EPOCH: u64 = 3; +// const GLOAS_FORK_EPOCH: u64 = 4; const SUGGESTED_FEE_RECIPIENT: [u8; 20] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; diff --git a/testing/simulator/src/fallback_sim.rs b/testing/simulator/src/fallback_sim.rs index 58875a985a0..6e0db52d755 100644 --- a/testing/simulator/src/fallback_sim.rs +++ b/testing/simulator/src/fallback_sim.rs @@ -29,6 +29,7 @@ const CAPELLA_FORK_EPOCH: u64 = 1; const DENEB_FORK_EPOCH: u64 = 2; // const ELECTRA_FORK_EPOCH: u64 = 3; // const FULU_FORK_EPOCH: u64 = 4; +// const GLOAS_FORK_EPOCH: u64 = 5; // Since simulator tests are non-deterministic and there is a non-zero chance of missed // attestations, define an acceptable network-wide attestation performance. diff --git a/validator_client/beacon_node_fallback/src/lib.rs b/validator_client/beacon_node_fallback/src/lib.rs index e5f4f1ab4c5..a3f60d2de04 100644 --- a/validator_client/beacon_node_fallback/src/lib.rs +++ b/validator_client/beacon_node_fallback/src/lib.rs @@ -359,6 +359,13 @@ impl CandidateBeaconNode { hint = UPDATE_REQUIRED_LOG_HINT, "Beacon node has mismatched Fulu fork epoch" ); + } else if beacon_node_spec.gloas_fork_epoch != spec.gloas_fork_epoch { + warn!( + endpoint = %self.beacon_node, + endpoint_gloas_fork_epoch = ?beacon_node_spec.gloas_fork_epoch, + hint = UPDATE_REQUIRED_LOG_HINT, + "Beacon node has mismatched Gloas fork epoch" + ); } Ok(()) diff --git a/validator_client/http_api/src/test_utils.rs b/validator_client/http_api/src/test_utils.rs index b15f08578dd..9a8784f2023 100644 --- a/validator_client/http_api/src/test_utils.rs +++ b/validator_client/http_api/src/test_utils.rs @@ -256,9 +256,9 @@ impl ApiTester { pub async fn test_get_lighthouse_spec(self) -> Self { let result = self .client - .get_lighthouse_spec::() + .get_lighthouse_spec::() .await - .map(|res| ConfigAndPreset::Fulu(res.data)) + .map(|res| ConfigAndPreset::Gloas(res.data)) .unwrap(); let expected = ConfigAndPreset::from_chain_spec::(&E::default_spec()); diff --git a/validator_client/http_api/src/tests.rs b/validator_client/http_api/src/tests.rs index c9a59521c57..b0780e74278 100644 --- a/validator_client/http_api/src/tests.rs +++ b/validator_client/http_api/src/tests.rs @@ -208,7 +208,12 @@ impl ApiTester { } pub async fn test_get_lighthouse_spec(self) -> Self { - let result = if self.spec.is_fulu_scheduled() { + let result = if self.spec.is_gloas_scheduled() { + self.client + .get_lighthouse_spec::() + .await + .map(|res| ConfigAndPreset::Gloas(res.data)) + } else if self.spec.is_fulu_scheduled() { self.client .get_lighthouse_spec::() .await diff --git a/validator_client/signing_method/src/web3signer.rs b/validator_client/signing_method/src/web3signer.rs index d286449d203..99fad103035 100644 --- a/validator_client/signing_method/src/web3signer.rs +++ b/validator_client/signing_method/src/web3signer.rs @@ -30,6 +30,7 @@ pub enum ForkName { Deneb, Electra, Fulu, + Gloas, } #[derive(Debug, PartialEq, Serialize)] @@ -113,6 +114,11 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> Web3SignerObject<'a, E, Pa block: None, block_header: Some(block.block_header()), }), + BeaconBlock::Gloas(_) => Ok(Web3SignerObject::BeaconBlock { + version: ForkName::Gloas, + block: None, + block_header: Some(block.block_header()), + }), } }