diff --git a/beacon_node/beacon_chain/src/beacon_block_streamer.rs b/beacon_node/beacon_chain/src/beacon_block_streamer.rs index f626a6d84fd..36debb5549f 100644 --- a/beacon_node/beacon_chain/src/beacon_block_streamer.rs +++ b/beacon_node/beacon_chain/src/beacon_block_streamer.rs @@ -3,7 +3,7 @@ use execution_layer::{ExecutionLayer, ExecutionPayloadBodyV1}; use slog::{crit, debug, Logger}; use std::collections::HashMap; use std::sync::Arc; -use store::{DatabaseBlock, ExecutionPayloadDeneb}; +use store::{DatabaseBlock, ExecutionPayloadDeneb, ExecutionPayloadEip6110}; use task_executor::TaskExecutor; use tokio::sync::{ mpsc::{self, UnboundedSender}, @@ -98,6 +98,7 @@ fn reconstruct_default_header_block( ForkName::Merge => ExecutionPayloadMerge::default().into(), ForkName::Capella => ExecutionPayloadCapella::default().into(), ForkName::Deneb => ExecutionPayloadDeneb::default().into(), + ForkName::Eip6110 => ExecutionPayloadEip6110::default().into(), ForkName::Base | ForkName::Altair => { return Err(Error::PayloadReconstruction(format!( "Block with fork variant {} has execution payload", diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 992ca830d7c..151def5963e 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -4515,7 +4515,10 @@ impl BeaconChain { // allows it to run concurrently with things like attestation packing. let prepare_payload_handle = match &state { BeaconState::Base(_) | BeaconState::Altair(_) => None, - BeaconState::Merge(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) => { + BeaconState::Merge(_) + | BeaconState::Capella(_) + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => { let prepare_payload_handle = get_execution_payload(self.clone(), &state, proposer_index, builder_params)?; Some(prepare_payload_handle) @@ -4860,6 +4863,39 @@ impl BeaconChain { proofs, ) } + BeaconState::Eip6110(_) => { + let (payload, kzg_commitments, blobs, proofs) = block_contents + .ok_or(BlockProductionError::MissingExecutionPayload)? + .deconstruct(); + ( + BeaconBlock::Eip6110(BeaconBlockEip6110 { + slot, + proposer_index, + parent_root, + state_root: Hash256::zero(), + body: BeaconBlockBodyEip6110 { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings: proposer_slashings.into(), + attester_slashings: attester_slashings.into(), + attestations: attestations.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)?, + }, + }), + blobs, + proofs, + ) + } }; let block = SignedBeaconBlock::from_block( @@ -5221,7 +5257,7 @@ impl BeaconChain { } else { let withdrawals = match self.spec.fork_name_at_slot::(prepare_slot) { ForkName::Base | ForkName::Altair | ForkName::Merge => None, - ForkName::Capella | ForkName::Deneb => { + ForkName::Capella | ForkName::Deneb | ForkName::Eip6110 => { let chain = self.clone(); self.spawn_blocking_handle( move || { diff --git a/beacon_node/beacon_chain/src/execution_payload.rs b/beacon_node/beacon_chain/src/execution_payload.rs index 51c7ec29901..d46028dd9b7 100644 --- a/beacon_node/beacon_chain/src/execution_payload.rs +++ b/beacon_node/beacon_chain/src/execution_payload.rs @@ -419,7 +419,7 @@ pub fn get_execution_payload< let latest_execution_payload_header_block_hash = state.latest_execution_payload_header()?.block_hash(); let withdrawals = match state { - &BeaconState::Capella(_) | &BeaconState::Deneb(_) => { + &BeaconState::Capella(_) | &BeaconState::Deneb(_) | &BeaconState::Eip6110(_) => { Some(get_expected_withdrawals(state, spec)?.into()) } &BeaconState::Merge(_) => None, diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 1d28f1c0a10..5176db32d8b 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -450,6 +450,9 @@ where let deneb_time = spec.deneb_fork_epoch.map(|epoch| { HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64() }); + let eip6110_time = spec.eip6110_fork_epoch.map(|epoch| { + HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64() + }); let trusted_setup: TrustedSetup = serde_json::from_reader(eth2_network_config::TRUSTED_SETUP) @@ -462,6 +465,7 @@ where DEFAULT_TERMINAL_BLOCK, shanghai_time, deneb_time, + eip6110_time, None, Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()), spec, @@ -489,6 +493,9 @@ where let deneb_time = spec.deneb_fork_epoch.map(|epoch| { HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64() }); + let eip6110_time = spec.eip6110_fork_epoch.map(|epoch| { + HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64() + }); let trusted_setup: TrustedSetup = serde_json::from_reader(eth2_network_config::TRUSTED_SETUP) .map_err(|e| format!("Unable to read trusted setup file: {}", e)) @@ -499,6 +506,7 @@ where DEFAULT_TERMINAL_BLOCK, shanghai_time, deneb_time, + eip6110_time, builder_threshold, Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()), spec.clone(), @@ -819,7 +827,7 @@ where | SignedBeaconBlock::Altair(_) | SignedBeaconBlock::Merge(_) | SignedBeaconBlock::Capella(_) => (signed_block, None), - SignedBeaconBlock::Deneb(_) => { + SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Eip6110(_) => { if let Some(blobs) = self .chain .proposal_blob_cache diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index 0a5d155f581..0d7e617899d 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -3,14 +3,15 @@ use crate::http::{ ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1, ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1, ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1, ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2, - ENGINE_GET_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, + ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V6110, ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, + ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V6110, }; use crate::BlobTxConversionError; use eth2::types::{SsePayloadAttributes, SsePayloadAttributesV1, SsePayloadAttributesV2}; pub use ethers_core::types::Transaction; use ethers_core::utils::rlp::{self, Decodable, Rlp}; use http::deposit_methods::RpcError; -pub use json_structures::{JsonWithdrawal, TransitionConfigurationV1}; +pub use json_structures::{JsonDepositReceipt, JsonWithdrawal, TransitionConfigurationV1}; use reqwest::StatusCode; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; @@ -19,11 +20,14 @@ use superstruct::superstruct; use types::beacon_block_body::KzgCommitments; use types::blob_sidecar::Blobs; pub use types::{ - Address, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader, - ExecutionPayloadRef, FixedVector, ForkName, Hash256, Transactions, Uint256, VariableList, - Withdrawal, Withdrawals, + Address, DepositReceipt, DepositReceipts, EthSpec, ExecutionBlockHash, ExecutionPayload, + ExecutionPayloadHeader, ExecutionPayloadRef, FixedVector, ForkName, Hash256, Transactions, + Uint256, VariableList, Withdrawal, Withdrawals, +}; +use types::{ + ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadEip6110, ExecutionPayloadMerge, + KzgProofs, }; -use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge, KzgProofs}; pub mod auth; pub mod http; @@ -53,6 +57,7 @@ pub enum Error { PayloadConversionLogicFlaw, SszError(ssz_types::Error), DeserializeWithdrawals(ssz_types::Error), + DeserializeDepositReceipts(ssz_types::Error), BuilderApi(builder_client::Error), IncorrectStateVariant, RequiredMethodUnsupported(&'static str), @@ -152,7 +157,7 @@ pub struct ExecutionBlock { /// Representation of an execution block with enough detail to reconstruct a payload. #[superstruct( - variants(Merge, Capella, Deneb), + variants(Merge, Capella, Deneb, Eip6110), variant_attributes( derive(Clone, Debug, PartialEq, Serialize, Deserialize,), serde(bound = "T: EthSpec", rename_all = "camelCase"), @@ -186,11 +191,13 @@ pub struct ExecutionBlockWithTransactions { #[serde(rename = "hash")] pub block_hash: ExecutionBlockHash, pub transactions: Vec, - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Capella, Deneb, Eip6110))] pub withdrawals: Vec, - #[superstruct(only(Deneb))] + #[superstruct(only(Deneb, Eip6110))] #[serde(with = "serde_utils::u256_hex_be")] pub excess_data_gas: Uint256, + #[superstruct(only(Eip6110))] + pub deposit_receipts: Vec, } impl TryFrom> for ExecutionBlockWithTransactions { @@ -269,6 +276,37 @@ impl TryFrom> for ExecutionBlockWithTransactions .collect(), excess_data_gas: block.excess_data_gas, }), + ExecutionPayload::Eip6110(block) => { + Self::Eip6110(ExecutionBlockWithTransactionsEip6110 { + parent_hash: block.parent_hash, + fee_recipient: block.fee_recipient, + state_root: block.state_root, + receipts_root: block.receipts_root, + logs_bloom: block.logs_bloom, + prev_randao: block.prev_randao, + block_number: block.block_number, + gas_limit: block.gas_limit, + gas_used: block.gas_used, + timestamp: block.timestamp, + extra_data: block.extra_data, + base_fee_per_gas: block.base_fee_per_gas, + block_hash: block.block_hash, + transactions: block + .transactions + .iter() + .map(|tx| Transaction::decode(&Rlp::new(tx))) + .collect::, _>>()?, + withdrawals: Vec::from(block.withdrawals) + .into_iter() + .map(|withdrawal| withdrawal.into()) + .collect(), + excess_data_gas: block.excess_data_gas, + deposit_receipts: Vec::from(block.deposit_receipts) + .into_iter() + .map(|deposit_receipt| deposit_receipt.into()) + .collect(), + }) + } }; Ok(json_payload) } @@ -363,7 +401,7 @@ pub struct ProposeBlindedBlockResponse { } #[superstruct( - variants(Merge, Capella, Deneb), + variants(Merge, Capella, Deneb, Eip6110), variant_attributes(derive(Clone, Debug, PartialEq),), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), @@ -378,8 +416,10 @@ pub struct GetPayloadResponse { pub execution_payload: ExecutionPayloadCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))] pub execution_payload: ExecutionPayloadDeneb, + #[superstruct(only(Eip6110), partial_getter(rename = "execution_payload_eip6110"))] + pub execution_payload: ExecutionPayloadEip6110, pub block_value: Uint256, - #[superstruct(only(Deneb))] + #[superstruct(only(Deneb, Eip6110))] pub blobs_bundle: BlobsBundleV1, } @@ -419,6 +459,11 @@ impl From> inner.block_value, Some(inner.blobs_bundle), ), + GetPayloadResponse::Eip6110(inner) => ( + ExecutionPayload::Eip6110(inner.execution_payload), + inner.block_value, + Some(inner.blobs_bundle), + ), } } } @@ -433,6 +478,7 @@ impl GetPayloadResponse { pub struct ExecutionPayloadBodyV1 { pub transactions: Transactions, pub withdrawals: Option>, + pub deposit_receipts: Option>, } impl ExecutionPayloadBodyV1 { @@ -518,6 +564,36 @@ impl ExecutionPayloadBodyV1 { )) } } + ExecutionPayloadHeader::Eip6110(header) => { + if let (Some(withdrawals), Some(deposit_receipts)) = + (self.withdrawals, self.deposit_receipts) + { + Ok(ExecutionPayload::Eip6110(ExecutionPayloadEip6110 { + 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, + excess_data_gas: header.excess_data_gas, + deposit_receipts, + })) + } else { + Err(format!( + "block {} is eip6110 but payload body doesn't have withdrawals and deposit_receipts", + header.block_hash + )) + } + } } } } @@ -534,6 +610,7 @@ pub struct EngineCapabilities { pub new_payload_v1: bool, pub new_payload_v2: bool, pub new_payload_v3: bool, + pub new_payload_v6110: bool, pub forkchoice_updated_v1: bool, pub forkchoice_updated_v2: bool, pub get_payload_bodies_by_hash_v1: bool, @@ -541,6 +618,7 @@ pub struct EngineCapabilities { pub get_payload_v1: bool, pub get_payload_v2: bool, pub get_payload_v3: bool, + pub get_payload_v6110: bool, pub exchange_transition_configuration_v1: bool, } @@ -556,6 +634,9 @@ impl EngineCapabilities { if self.new_payload_v3 { response.push(ENGINE_NEW_PAYLOAD_V3); } + if self.new_payload_v6110 { + response.push(ENGINE_NEW_PAYLOAD_V6110); + } if self.forkchoice_updated_v1 { response.push(ENGINE_FORKCHOICE_UPDATED_V1); } @@ -577,6 +658,9 @@ impl EngineCapabilities { if self.get_payload_v3 { response.push(ENGINE_GET_PAYLOAD_V3); } + if self.get_payload_v6110 { + response.push(ENGINE_GET_PAYLOAD_V6110); + } if self.exchange_transition_configuration_v1 { response.push(ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1); } diff --git a/beacon_node/execution_layer/src/engine_api/http.rs b/beacon_node/execution_layer/src/engine_api/http.rs index 8e403b2beab..ce3696120c5 100644 --- a/beacon_node/execution_layer/src/engine_api/http.rs +++ b/beacon_node/execution_layer/src/engine_api/http.rs @@ -33,11 +33,13 @@ pub const ETH_SYNCING_TIMEOUT: Duration = Duration::from_secs(1); pub const ENGINE_NEW_PAYLOAD_V1: &str = "engine_newPayloadV1"; pub const ENGINE_NEW_PAYLOAD_V2: &str = "engine_newPayloadV2"; pub const ENGINE_NEW_PAYLOAD_V3: &str = "engine_newPayloadV3"; +pub const ENGINE_NEW_PAYLOAD_V6110: &str = "engine_newPayloadV6110"; pub const ENGINE_NEW_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(8); pub const ENGINE_GET_PAYLOAD_V1: &str = "engine_getPayloadV1"; pub const ENGINE_GET_PAYLOAD_V2: &str = "engine_getPayloadV2"; pub const ENGINE_GET_PAYLOAD_V3: &str = "engine_getPayloadV3"; +pub const ENGINE_GET_PAYLOAD_V6110: &str = "engine_getPayloadV6110"; pub const ENGINE_GET_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(2); pub const ENGINE_FORKCHOICE_UPDATED_V1: &str = "engine_forkchoiceUpdatedV1"; @@ -65,9 +67,11 @@ pub static LIGHTHOUSE_CAPABILITIES: &[&str] = &[ ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, + ENGINE_NEW_PAYLOAD_V6110, ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2, ENGINE_GET_PAYLOAD_V3, + ENGINE_GET_PAYLOAD_V6110, ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1, @@ -83,6 +87,7 @@ pub static PRE_CAPELLA_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilit new_payload_v1: true, new_payload_v2: false, new_payload_v3: false, + new_payload_v6110: false, forkchoice_updated_v1: true, forkchoice_updated_v2: false, get_payload_bodies_by_hash_v1: false, @@ -90,6 +95,7 @@ pub static PRE_CAPELLA_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilit get_payload_v1: true, get_payload_v2: false, get_payload_v3: false, + get_payload_v6110: false, exchange_transition_configuration_v1: true, }; @@ -762,6 +768,14 @@ impl HttpJsonRpc { ) .await?, ), + ForkName::Eip6110 => ExecutionBlockWithTransactions::Eip6110( + self.rpc_request( + ETH_GET_BLOCK_BY_HASH, + params, + ETH_GET_BLOCK_BY_HASH_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?, + ), ForkName::Base | ForkName::Altair => { return Err(Error::UnsupportedForkVariant(format!( "called get_block_by_hash_with_txns with fork {:?}", @@ -822,6 +836,23 @@ impl HttpJsonRpc { Ok(response.into()) } + pub async fn new_payload_v6110( + &self, + execution_payload: ExecutionPayload, + ) -> Result { + let params = json!([JsonExecutionPayload::from(execution_payload)]); + + let response: JsonPayloadStatusV1 = self + .rpc_request( + ENGINE_NEW_PAYLOAD_V6110, + params, + ENGINE_NEW_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + + Ok(response.into()) + } + pub async fn get_payload_v1( &self, payload_id: PayloadId, @@ -873,7 +904,7 @@ impl HttpJsonRpc { .await?; Ok(JsonGetPayloadResponse::V2(response).into()) } - ForkName::Base | ForkName::Altair | ForkName::Deneb => Err( + ForkName::Base | ForkName::Altair | ForkName::Deneb | ForkName::Eip6110 => Err( Error::UnsupportedForkVariant(format!("called get_payload_v2 with {}", fork_name)), ), } @@ -917,8 +948,62 @@ impl HttpJsonRpc { .await?; Ok(JsonGetPayloadResponse::V3(response).into()) } + ForkName::Base | ForkName::Altair | ForkName::Eip6110 => Err( + Error::UnsupportedForkVariant(format!("called get_payload_v3 with {}", fork_name)), + ), + } + } + + pub async fn get_payload_v6110( + &self, + fork_name: ForkName, + payload_id: PayloadId, + ) -> Result, Error> { + let params = json!([JsonPayloadIdRequest::from(payload_id)]); + + match fork_name { + ForkName::Merge => { + let response: JsonGetPayloadResponseV1 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V2, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + Ok(JsonGetPayloadResponse::V1(response).into()) + } + ForkName::Capella => { + let response: JsonGetPayloadResponseV2 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V2, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + Ok(JsonGetPayloadResponse::V2(response).into()) + } + ForkName::Deneb => { + let response: JsonGetPayloadResponseV3 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V3, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + Ok(JsonGetPayloadResponse::V3(response).into()) + } + ForkName::Eip6110 => { + let response: JsonGetPayloadResponseV6110 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V6110, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + Ok(JsonGetPayloadResponse::V6110(response).into()) + } ForkName::Base | ForkName::Altair => Err(Error::UnsupportedForkVariant(format!( - "called get_payload_v3 with {}", + "called get_payload_v6110 with {}", fork_name ))), } @@ -1051,6 +1136,7 @@ impl HttpJsonRpc { new_payload_v1: capabilities.contains(ENGINE_NEW_PAYLOAD_V1), new_payload_v2: capabilities.contains(ENGINE_NEW_PAYLOAD_V2), new_payload_v3: capabilities.contains(ENGINE_NEW_PAYLOAD_V3), + new_payload_v6110: capabilities.contains(ENGINE_NEW_PAYLOAD_V6110), forkchoice_updated_v1: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V1), forkchoice_updated_v2: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V2), get_payload_bodies_by_hash_v1: capabilities @@ -1060,6 +1146,7 @@ impl HttpJsonRpc { get_payload_v1: capabilities.contains(ENGINE_GET_PAYLOAD_V1), get_payload_v2: capabilities.contains(ENGINE_GET_PAYLOAD_V2), get_payload_v3: capabilities.contains(ENGINE_GET_PAYLOAD_V3), + get_payload_v6110: capabilities.contains(ENGINE_GET_PAYLOAD_V6110), exchange_transition_configuration_v1: capabilities .contains(ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1), }), @@ -1101,7 +1188,9 @@ impl HttpJsonRpc { execution_payload: ExecutionPayload, ) -> Result { let engine_capabilities = self.get_engine_capabilities(None).await?; - if engine_capabilities.new_payload_v3 { + if engine_capabilities.new_payload_v6110 { + self.new_payload_v6110(execution_payload).await + } else if engine_capabilities.new_payload_v3 { self.new_payload_v3(execution_payload).await } else if engine_capabilities.new_payload_v2 { self.new_payload_v2(execution_payload).await @@ -1120,7 +1209,9 @@ impl HttpJsonRpc { payload_id: PayloadId, ) -> Result, Error> { let engine_capabilities = self.get_engine_capabilities(None).await?; - if engine_capabilities.get_payload_v3 { + if engine_capabilities.get_payload_v6110 { + self.get_payload_v6110(fork_name, payload_id).await + } else if engine_capabilities.get_payload_v3 { self.get_payload_v3(fork_name, payload_id).await } else if engine_capabilities.get_payload_v2 { self.get_payload_v2(fork_name, payload_id).await 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 d541107d28f..d0fa89f262f 100644 --- a/beacon_node/execution_layer/src/engine_api/json_structures.rs +++ b/beacon_node/execution_layer/src/engine_api/json_structures.rs @@ -1,4 +1,6 @@ use super::*; +use eth2::types::PublicKeyBytes; +use eth2::types::SignatureBytes; use serde::{Deserialize, Serialize}; use strum::EnumString; use superstruct::superstruct; @@ -6,7 +8,8 @@ use types::beacon_block_body::KzgCommitments; use types::blob_sidecar::Blobs; use types::{ EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, - ExecutionPayloadMerge, FixedVector, Transactions, Unsigned, VariableList, Withdrawal, + ExecutionPayloadEip6110, ExecutionPayloadMerge, FixedVector, Transactions, Unsigned, + VariableList, Withdrawal, }; #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -63,7 +66,7 @@ pub struct JsonPayloadIdResponse { } #[superstruct( - variants(V1, V2, V3), + variants(V1, V2, V3, V6110), variant_attributes( derive(Debug, PartialEq, Default, Serialize, Deserialize,), serde(bound = "T: EthSpec", rename_all = "camelCase"), @@ -96,11 +99,13 @@ pub struct JsonExecutionPayload { pub block_hash: ExecutionBlockHash, #[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")] pub transactions: Transactions, - #[superstruct(only(V2, V3))] + #[superstruct(only(V2, V3, V6110))] pub withdrawals: VariableList, - #[superstruct(only(V3))] + #[superstruct(only(V3, V6110))] #[serde(with = "serde_utils::u256_hex_be")] pub excess_data_gas: Uint256, + #[superstruct(only(V6110))] + pub deposit_receipts: VariableList, } impl From> for JsonExecutionPayloadV1 { @@ -176,6 +181,39 @@ impl From> for JsonExecutionPayloadV3 { } } } +impl From> for JsonExecutionPayloadV6110 { + fn from(payload: ExecutionPayloadEip6110) -> Self { + JsonExecutionPayloadV6110 { + 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(), + excess_data_gas: payload.excess_data_gas, + deposit_receipts: payload + .deposit_receipts + .into_iter() + .map(Into::into) + .collect::>() + .into(), + } + } +} impl From> for JsonExecutionPayload { fn from(execution_payload: ExecutionPayload) -> Self { @@ -183,6 +221,7 @@ impl From> for JsonExecutionPayload { ExecutionPayload::Merge(payload) => JsonExecutionPayload::V1(payload.into()), ExecutionPayload::Capella(payload) => JsonExecutionPayload::V2(payload.into()), ExecutionPayload::Deneb(payload) => JsonExecutionPayload::V3(payload.into()), + ExecutionPayload::Eip6110(payload) => JsonExecutionPayload::V6110(payload.into()), } } } @@ -260,6 +299,39 @@ impl From> for ExecutionPayloadDeneb { } } } +impl From> for ExecutionPayloadEip6110 { + fn from(payload: JsonExecutionPayloadV6110) -> Self { + ExecutionPayloadEip6110 { + 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(), + excess_data_gas: payload.excess_data_gas, + deposit_receipts: payload + .deposit_receipts + .into_iter() + .map(Into::into) + .collect::>() + .into(), + } + } +} impl From> for ExecutionPayload { fn from(json_execution_payload: JsonExecutionPayload) -> Self { @@ -267,12 +339,13 @@ impl From> for ExecutionPayload { JsonExecutionPayload::V1(payload) => ExecutionPayload::Merge(payload.into()), JsonExecutionPayload::V2(payload) => ExecutionPayload::Capella(payload.into()), JsonExecutionPayload::V3(payload) => ExecutionPayload::Deneb(payload.into()), + JsonExecutionPayload::V6110(payload) => ExecutionPayload::Eip6110(payload.into()), } } } #[superstruct( - variants(V1, V2, V3), + variants(V1, V2, V3, V6110), variant_attributes( derive(Debug, PartialEq, Serialize, Deserialize), serde(bound = "T: EthSpec", rename_all = "camelCase") @@ -289,9 +362,11 @@ pub struct JsonGetPayloadResponse { pub execution_payload: JsonExecutionPayloadV2, #[superstruct(only(V3), partial_getter(rename = "execution_payload_v3"))] pub execution_payload: JsonExecutionPayloadV3, + #[superstruct(only(V6110), partial_getter(rename = "execution_payload_v6110"))] + pub execution_payload: JsonExecutionPayloadV6110, #[serde(with = "serde_utils::u256_hex_be")] pub block_value: Uint256, - #[superstruct(only(V3))] + #[superstruct(only(V3, V6110))] pub blobs_bundle: JsonBlobsBundleV1, } @@ -317,6 +392,13 @@ impl From> for GetPayloadResponse { blobs_bundle: response.blobs_bundle.into(), }) } + JsonGetPayloadResponse::V6110(response) => { + GetPayloadResponse::Eip6110(GetPayloadResponseEip6110 { + execution_payload: response.execution_payload.into(), + block_value: response.block_value, + blobs_bundle: response.blobs_bundle.into(), + }) + } } } } @@ -355,6 +437,42 @@ impl From for Withdrawal { } } +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct JsonDepositReceipt { + pub pubkey: PublicKeyBytes, + pub withdrawal_credentials: Hash256, + #[serde(with = "serde_utils::u64_hex_be")] + pub amount: u64, + pub signature: SignatureBytes, + #[serde(with = "serde_utils::u64_hex_be")] + pub index: u64, +} + +impl From for JsonDepositReceipt { + fn from(deposit_receipt: DepositReceipt) -> Self { + Self { + pubkey: deposit_receipt.pubkey.decompress().unwrap().into(), + withdrawal_credentials: deposit_receipt.withdrawal_credentials, + amount: deposit_receipt.amount, + signature: deposit_receipt.signature, + index: deposit_receipt.index, + } + } +} + +impl From for DepositReceipt { + fn from(json_deposit_receipt: JsonDepositReceipt) -> Self { + Self { + pubkey: json_deposit_receipt.pubkey, + withdrawal_credentials: json_deposit_receipt.withdrawal_credentials, + amount: json_deposit_receipt.amount, + signature: json_deposit_receipt.signature, + index: json_deposit_receipt.index, + } + } +} + #[superstruct( variants(V1, V2), variant_attributes( @@ -599,6 +717,7 @@ pub struct JsonExecutionPayloadBodyV1 { #[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")] pub transactions: Transactions, pub withdrawals: Option>, + pub deposit_receipts: Option>, } impl From> for ExecutionPayloadBodyV1 { @@ -613,6 +732,14 @@ impl From> for ExecutionPayloadBodyV1< .collect::>(), ) }), + deposit_receipts: value.deposit_receipts.map(|json_receipts| { + DepositReceipts::::from( + json_receipts + .into_iter() + .map(Into::into) + .collect::>(), + ) + }), } } } diff --git a/beacon_node/execution_layer/src/lib.rs b/beacon_node/execution_layer/src/lib.rs index 3047906c189..ff28c5b20e4 100644 --- a/beacon_node/execution_layer/src/lib.rs +++ b/beacon_node/execution_layer/src/lib.rs @@ -48,7 +48,8 @@ use types::transaction::{AccessTuple, BlobTransaction, EcdsaSignature, SignedBlo use types::{AbstractExecPayload, BeaconStateError, ExecPayload, VersionedHash}; use types::{ BlindedPayload, BlockType, ChainSpec, Epoch, ExecutionBlockHash, ExecutionPayload, - ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge, ForkName, + ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadEip6110, ExecutionPayloadMerge, + ForkName, }; use types::{KzgProofs, Withdrawals}; use types::{ @@ -244,7 +245,7 @@ impl> BlockProposalContents BlockProposalContents::PayloadAndBlobs { + ForkName::Deneb | ForkName::Eip6110 => BlockProposalContents::PayloadAndBlobs { payload: Payload::default_at_fork(fork_name)?, block_value: Uint256::zero(), blobs: VariableList::default(), @@ -1702,6 +1703,7 @@ impl ExecutionLayer { ForkName::Merge => ExecutionPayloadMerge::default().into(), ForkName::Capella => ExecutionPayloadCapella::default().into(), ForkName::Deneb => ExecutionPayloadDeneb::default().into(), + ForkName::Eip6110 => ExecutionPayloadEip6110::default().into(), ForkName::Base | ForkName::Altair => { return Err(Error::InvalidForkForPayload); } @@ -1759,6 +1761,7 @@ impl ExecutionLayer { ForkName::Merge => Ok(Some(ExecutionPayloadMerge::default().into())), ForkName::Capella => Ok(Some(ExecutionPayloadCapella::default().into())), ForkName::Deneb => Ok(Some(ExecutionPayloadDeneb::default().into())), + ForkName::Eip6110 => Ok(Some(ExecutionPayloadEip6110::default().into())), ForkName::Base | ForkName::Altair => Err(ApiError::UnsupportedForkVariant( format!("called get_payload_by_hash_from_engine with {}", fork), )), @@ -1859,6 +1862,45 @@ impl ExecutionLayer { excess_data_gas: deneb_block.excess_data_gas, }) } + ExecutionBlockWithTransactions::Eip6110(eip6110_block) => { + let withdrawals = VariableList::new( + eip6110_block + .withdrawals + .into_iter() + .map(Into::into) + .collect(), + ) + .map_err(ApiError::DeserializeWithdrawals)?; + + let deposit_receipts = VariableList::new( + eip6110_block + .deposit_receipts + .into_iter() + .map(Into::into) + .collect(), + ) + .map_err(ApiError::DeserializeDepositReceipts)?; + + ExecutionPayload::Eip6110(ExecutionPayloadEip6110 { + parent_hash: eip6110_block.parent_hash, + fee_recipient: eip6110_block.fee_recipient, + state_root: eip6110_block.state_root, + receipts_root: eip6110_block.receipts_root, + logs_bloom: eip6110_block.logs_bloom, + prev_randao: eip6110_block.prev_randao, + block_number: eip6110_block.block_number, + gas_limit: eip6110_block.gas_limit, + gas_used: eip6110_block.gas_used, + timestamp: eip6110_block.timestamp, + extra_data: eip6110_block.extra_data, + base_fee_per_gas: eip6110_block.base_fee_per_gas, + block_hash: eip6110_block.block_hash, + transactions: convert_transactions(eip6110_block.transactions)?, + withdrawals, + excess_data_gas: eip6110_block.excess_data_gas, + deposit_receipts, + }) + } }; Ok(Some(payload)) 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 5e5508b6f8e..02c5762923f 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 types::consts::deneb::BLOB_TX_TYPE; use types::transaction::{BlobTransaction, EcdsaSignature, SignedBlobTransaction}; use types::{ Blob, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella, - ExecutionPayloadDeneb, ExecutionPayloadMerge, ForkName, Hash256, Transaction, Transactions, - Uint256, + ExecutionPayloadDeneb, ExecutionPayloadEip6110, ExecutionPayloadMerge, ForkName, Hash256, + Transaction, Transactions, Uint256, }; const GAS_LIMIT: u64 = 16384; @@ -126,6 +126,7 @@ pub struct ExecutionBlockGenerator { */ pub shanghai_time: Option, // withdrawals pub deneb_time: Option, // 4844 + pub eip6110_time: Option, // 6110 /* * deneb stuff */ @@ -140,6 +141,7 @@ impl ExecutionBlockGenerator { terminal_block_hash: ExecutionBlockHash, shanghai_time: Option, deneb_time: Option, + eip6110_time: Option, kzg: Option, ) -> Self { let mut gen = Self { @@ -155,6 +157,7 @@ impl ExecutionBlockGenerator { payload_ids: <_>::default(), shanghai_time, deneb_time, + eip6110_time, blobs_bundles: <_>::default(), kzg: kzg.map(Arc::new), }; @@ -189,11 +192,14 @@ impl ExecutionBlockGenerator { } pub fn get_fork_at_timestamp(&self, timestamp: u64) -> ForkName { - match self.deneb_time { - Some(fork_time) if timestamp >= fork_time => ForkName::Deneb, - _ => match self.shanghai_time { - Some(fork_time) if timestamp >= fork_time => ForkName::Capella, - _ => ForkName::Merge, + match self.eip6110_time { + Some(fork_time) if timestamp >= fork_time => ForkName::Eip6110, + _ => match self.deneb_time { + Some(fork_time) if timestamp >= fork_time => ForkName::Deneb, + _ => match self.shanghai_time { + Some(fork_time) if timestamp >= fork_time => ForkName::Capella, + _ => ForkName::Merge, + }, }, } } @@ -576,6 +582,28 @@ impl ExecutionBlockGenerator { excess_data_gas: Uint256::one(), }) } + ForkName::Eip6110 => { + ExecutionPayload::Eip6110(ExecutionPayloadEip6110 { + parent_hash: forkchoice_state.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: 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::one(), + // FIXME(4844): maybe this should be set to something? + block_hash: ExecutionBlockHash::zero(), + transactions: vec![].into(), + withdrawals: pa.withdrawals.clone().into(), + excess_data_gas: Uint256::one(), + deposit_receipts: vec![].into(), + }) + } _ => unreachable!(), } } @@ -583,7 +611,7 @@ impl ExecutionBlockGenerator { match execution_payload.fork_name() { ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {} - ForkName::Deneb => { + ForkName::Deneb | ForkName::Eip6110 => { // get random number between 0 and Max Blobs let num_blobs = rand::random::() % T::max_blobs_per_block(); let (bundle, transactions) = self.generate_random_blobs(num_blobs)?; @@ -769,6 +797,7 @@ mod test { None, None, None, + None, ); for i in 0..=TERMINAL_BLOCK { 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 cd2024797f0..875f86df912 100644 --- a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs +++ b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs @@ -93,7 +93,10 @@ pub async fn handle_rpc( .unwrap()) } } - ENGINE_NEW_PAYLOAD_V1 | ENGINE_NEW_PAYLOAD_V2 | ENGINE_NEW_PAYLOAD_V3 => { + ENGINE_NEW_PAYLOAD_V1 + | ENGINE_NEW_PAYLOAD_V2 + | ENGINE_NEW_PAYLOAD_V3 + | ENGINE_NEW_PAYLOAD_V6110 => { let request = match method { ENGINE_NEW_PAYLOAD_V1 => JsonExecutionPayload::V1( get_param::>(params, 0) @@ -117,6 +120,21 @@ pub async fn handle_rpc( }) }) .map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?, + ENGINE_NEW_PAYLOAD_V6110 => get_param::>(params, 0) + .map(|jep| JsonExecutionPayload::V6110(jep)) + .or_else(|_| { + get_param::>(params, 0) + .map(|jep| JsonExecutionPayload::V3(jep)) + .or_else(|_| { + get_param::>(params, 0) + .map(|jep| JsonExecutionPayload::V2(jep)) + .or_else(|_| { + get_param::>(params, 0) + .map(|jep| JsonExecutionPayload::V1(jep)) + }) + }) + }) + .map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?, _ => unreachable!(), }; @@ -180,6 +198,44 @@ pub async fn handle_rpc( )); } } + ForkName::Eip6110 => { + if method == ENGINE_NEW_PAYLOAD_V1 + || method == ENGINE_NEW_PAYLOAD_V2 + || method == ENGINE_NEW_PAYLOAD_V3 + { + return Err(( + format!("{} called after eip6110 fork!", method), + GENERIC_ERROR_CODE, + )); + } + if matches!(request, JsonExecutionPayload::V1(_)) { + return Err(( + format!( + "{} called with `ExecutionPayloadV1` after eip6110 fork!", + method + ), + GENERIC_ERROR_CODE, + )); + } + if matches!(request, JsonExecutionPayload::V2(_)) { + return Err(( + format!( + "{} called with `ExecutionPayloadV2` after eip6110 fork!", + method + ), + GENERIC_ERROR_CODE, + )); + } + if matches!(request, JsonExecutionPayload::V3(_)) { + return Err(( + format!( + "{} called with `ExecutionPayloadV3` after eip6110 fork!", + method + ), + GENERIC_ERROR_CODE, + )); + } + } _ => unreachable!(), }; @@ -215,7 +271,10 @@ pub async fn handle_rpc( Ok(serde_json::to_value(JsonPayloadStatusV1::from(response)).unwrap()) } - ENGINE_GET_PAYLOAD_V1 | ENGINE_GET_PAYLOAD_V2 | ENGINE_GET_PAYLOAD_V3 => { + ENGINE_GET_PAYLOAD_V1 + | ENGINE_GET_PAYLOAD_V2 + | ENGINE_GET_PAYLOAD_V3 + | ENGINE_GET_PAYLOAD_V6110 => { let request: JsonPayloadIdRequest = get_param(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?; let id = request.into(); @@ -259,6 +318,21 @@ pub async fn handle_rpc( FORK_REQUEST_MISMATCH_ERROR_CODE, )); } + // validate method called correctly according to eip6110 fork time + if ctx + .execution_block_generator + .read() + .get_fork_at_timestamp(response.timestamp()) + == ForkName::Eip6110 + && (method == ENGINE_GET_PAYLOAD_V1 + || method == ENGINE_GET_PAYLOAD_V2 + || method == ENGINE_GET_PAYLOAD_V3) + { + return Err(( + format!("{} called after eip6110 fork!", method), + FORK_REQUEST_MISMATCH_ERROR_CODE, + )); + } match method { ENGINE_GET_PAYLOAD_V1 => { @@ -309,6 +383,49 @@ pub async fn handle_rpc( }) .unwrap() } + _ => unreachable!(), + }), + ENGINE_GET_PAYLOAD_V6110 => Ok(match JsonExecutionPayload::from(response) { + JsonExecutionPayload::V1(execution_payload) => { + serde_json::to_value(JsonGetPayloadResponseV1 { + execution_payload, + block_value: DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI.into(), + }) + .unwrap() + } + JsonExecutionPayload::V2(execution_payload) => { + serde_json::to_value(JsonGetPayloadResponseV2 { + execution_payload, + block_value: DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI.into(), + }) + .unwrap() + } + JsonExecutionPayload::V3(execution_payload) => { + serde_json::to_value(JsonGetPayloadResponseV3 { + execution_payload, + block_value: DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI.into(), + blobs_bundle: maybe_blobs + .ok_or(( + "No blobs returned despite V3 Payload".to_string(), + GENERIC_ERROR_CODE, + ))? + .into(), + }) + .unwrap() + } + JsonExecutionPayload::V6110(execution_payload) => { + serde_json::to_value(JsonGetPayloadResponseV6110 { + execution_payload, + block_value: DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI.into(), + blobs_bundle: maybe_blobs + .ok_or(( + "No blobs returned despite V6110 Payload".to_string(), + GENERIC_ERROR_CODE, + ))? + .into(), + }) + .unwrap() + } }), _ => unreachable!(), } @@ -495,6 +612,9 @@ pub async fn handle_rpc( .withdrawals() .ok() .map(|withdrawals| VariableList::from(withdrawals.clone())), + deposit_receipts: block.deposit_receipts().ok().map( + |deposit_receipts| VariableList::from(deposit_receipts.clone()), + ), })); } None => response.push(None), 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 fe5414028cd..e9fa3cdb7cc 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -405,7 +405,7 @@ impl mev_rs::BlindedBlockProvider for MockBuilder { let payload_attributes = match fork { ForkName::Merge => PayloadAttributes::new(timestamp, *prev_randao, fee_recipient, None), // the withdrawals root is filled in by operations - ForkName::Capella | ForkName::Deneb => { + ForkName::Capella | ForkName::Deneb | ForkName::Eip6110 => { PayloadAttributes::new(timestamp, *prev_randao, fee_recipient, Some(vec![])) } ForkName::Base | ForkName::Altair => { @@ -452,7 +452,7 @@ impl mev_rs::BlindedBlockProvider for MockBuilder { value: to_ssz_rs(&Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI))?, public_key: self.builder_sk.public_key(), }), - ForkName::Base | ForkName::Altair | ForkName::Deneb => { + ForkName::Base | ForkName::Altair | ForkName::Deneb | ForkName::Eip6110 => { return Err(BlindedBlockProviderError::Custom(format!( "Unsupported fork: {}", fork 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 0c6f5ce666e..571e466ba41 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 @@ -31,6 +31,7 @@ impl MockExecutionLayer { None, None, None, + None, Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()), spec, None, @@ -44,6 +45,7 @@ impl MockExecutionLayer { terminal_block: u64, shanghai_time: Option, deneb_time: Option, + eip6110_time: Option, builder_threshold: Option, jwt_key: Option, spec: ChainSpec, @@ -61,6 +63,7 @@ impl MockExecutionLayer { spec.terminal_block_hash, shanghai_time, deneb_time, + eip6110_time, kzg, ); diff --git a/beacon_node/execution_layer/src/test_utils/mod.rs b/beacon_node/execution_layer/src/test_utils/mod.rs index 72cd0e81e4e..0ec826fc7ca 100644 --- a/beacon_node/execution_layer/src/test_utils/mod.rs +++ b/beacon_node/execution_layer/src/test_utils/mod.rs @@ -39,6 +39,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities { new_payload_v1: true, new_payload_v2: true, new_payload_v3: true, + new_payload_v6110: true, forkchoice_updated_v1: true, forkchoice_updated_v2: true, get_payload_bodies_by_hash_v1: true, @@ -46,6 +47,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities { get_payload_v1: true, get_payload_v2: true, get_payload_v3: true, + get_payload_v6110: true, exchange_transition_configuration_v1: true, }; @@ -64,6 +66,7 @@ pub struct MockExecutionConfig { pub terminal_block_hash: ExecutionBlockHash, pub shanghai_time: Option, pub deneb_time: Option, + pub eip6110_time: Option, } impl Default for MockExecutionConfig { @@ -76,6 +79,7 @@ impl Default for MockExecutionConfig { server_config: Config::default(), shanghai_time: None, deneb_time: None, + eip6110_time: None, } } } @@ -97,6 +101,7 @@ impl MockServer { ExecutionBlockHash::zero(), None, // FIXME(capella): should this be the default? None, // FIXME(deneb): should this be the default? + None, // FIXME(eip6110): should this be the default? None, // FIXME(deneb): should this be the default? ) } @@ -114,6 +119,7 @@ impl MockServer { server_config, shanghai_time, deneb_time, + eip6110_time, } = config; let last_echo_request = Arc::new(RwLock::new(None)); let preloaded_responses = Arc::new(Mutex::new(vec![])); @@ -123,6 +129,7 @@ impl MockServer { terminal_block_hash, shanghai_time, deneb_time, + eip6110_time, kzg, ); @@ -185,6 +192,7 @@ impl MockServer { terminal_block_hash: ExecutionBlockHash, shanghai_time: Option, deneb_time: Option, + eip6110_time: Option, kzg: Option, ) -> Self { Self::new_with_config( @@ -197,6 +205,7 @@ impl MockServer { terminal_block_hash, shanghai_time, deneb_time, + eip6110_time, }, kzg, ) diff --git a/beacon_node/http_api/src/build_block_contents.rs b/beacon_node/http_api/src/build_block_contents.rs index d6c5ada0071..69f3c282d50 100644 --- a/beacon_node/http_api/src/build_block_contents.rs +++ b/beacon_node/http_api/src/build_block_contents.rs @@ -14,7 +14,7 @@ pub fn build_block_contents { Ok(BlockContents::Block(block)) } - ForkName::Deneb => { + ForkName::Deneb | ForkName::Eip6110 => { let block_root = &block.canonical_root(); if let Some(blob_sidecars) = chain.proposal_blob_cache.pop(block_root) { let block_and_blobs = BeaconBlockAndBlobSidecars { diff --git a/beacon_node/http_api/src/publish_blocks.rs b/beacon_node/http_api/src/publish_blocks.rs index d722cf6c9b7..a58dd54931a 100644 --- a/beacon_node/http_api/src/publish_blocks.rs +++ b/beacon_node/http_api/src/publish_blocks.rs @@ -68,7 +68,7 @@ pub async fn publish_block( crate::publish_pubsub_message(network_tx, PubsubMessage::BeaconBlock(block.clone()))?; block.into() } - SignedBeaconBlock::Deneb(_) => { + SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Eip6110(_) => { crate::publish_pubsub_message(network_tx, PubsubMessage::BeaconBlock(block.clone()))?; if let Some(signed_blobs) = maybe_blobs { for (blob_index, blob) in signed_blobs.clone().into_iter().enumerate() { diff --git a/beacon_node/lighthouse_network/src/config.rs b/beacon_node/lighthouse_network/src/config.rs index e55acda70c9..08baaa4ce8b 100644 --- a/beacon_node/lighthouse_network/src/config.rs +++ b/beacon_node/lighthouse_network/src/config.rs @@ -428,7 +428,11 @@ pub fn gossipsub_config(network_load: u8, fork_context: Arc) -> Gos match fork_context.current_fork() { // according to: https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/p2p-interface.md#the-gossip-domain-gossipsub // the derivation of the message-id remains the same in the merge and for eip 4844. - ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + ForkName::Altair + | ForkName::Merge + | ForkName::Capella + | ForkName::Deneb + | ForkName::Eip6110 => { let topic_len_bytes = topic_bytes.len().to_le_bytes(); let mut vec = Vec::with_capacity( prefix.len() + topic_len_bytes.len() + topic_bytes.len() + message.data.len(), diff --git a/beacon_node/lighthouse_network/src/rpc/codec/base.rs b/beacon_node/lighthouse_network/src/rpc/codec/base.rs index c2ad2320d9f..c2bebb699eb 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec/base.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec/base.rs @@ -195,11 +195,13 @@ mod tests { let merge_fork_epoch = Epoch::new(2); let capella_fork_epoch = Epoch::new(3); let deneb_fork_epoch = Epoch::new(4); + let eip6110_fork_epoch = Epoch::new(5); chain_spec.altair_fork_epoch = Some(altair_fork_epoch); chain_spec.bellatrix_fork_epoch = Some(merge_fork_epoch); chain_spec.capella_fork_epoch = Some(capella_fork_epoch); chain_spec.deneb_fork_epoch = Some(deneb_fork_epoch); + chain_spec.eip6110_fork_epoch = Some(eip6110_fork_epoch); let current_slot = match fork_name { ForkName::Base => Slot::new(0), @@ -207,6 +209,7 @@ mod tests { ForkName::Merge => merge_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Capella => capella_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Deneb => deneb_fork_epoch.start_slot(Spec::slots_per_epoch()), + ForkName::Eip6110 => eip6110_fork_epoch.start_slot(Spec::slots_per_epoch()), }; ForkContext::new::(current_slot, Hash256::zero(), &chain_spec) } diff --git a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs index e9622f24a48..f4e8f486a7a 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs @@ -19,7 +19,7 @@ use types::{light_client_bootstrap::LightClientBootstrap, BlobSidecar}; use types::{ EthSpec, ForkContext, ForkName, Hash256, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockCapella, SignedBeaconBlockDeneb, - SignedBeaconBlockMerge, + SignedBeaconBlockEip6110, SignedBeaconBlockMerge, }; use unsigned_varint::codec::Uvi; @@ -419,6 +419,10 @@ fn context_bytes( return match **ref_box_block { // NOTE: If you are adding another fork type here, be sure to modify the // `fork_context.to_context_bytes()` function to support it as well! + SignedBeaconBlock::Eip6110 { .. } => { + // Eip6110 context being `None` implies that "merge never happened". + fork_context.to_context_bytes(ForkName::Eip6110) + } SignedBeaconBlock::Deneb { .. } => { // Deneb context being `None` implies that "merge never happened". fork_context.to_context_bytes(ForkName::Deneb) @@ -667,6 +671,11 @@ fn handle_v2_response( decoded_buffer, )?), )))), + ForkName::Eip6110 => Ok(Some(RPCResponse::BlocksByRange(Arc::new( + SignedBeaconBlock::Eip6110(SignedBeaconBlockEip6110::from_ssz_bytes( + decoded_buffer, + )?), + )))), }, Protocol::BlocksByRoot => match fork_name { ForkName::Altair => Ok(Some(RPCResponse::BlocksByRoot(Arc::new( @@ -692,6 +701,11 @@ fn handle_v2_response( decoded_buffer, )?), )))), + ForkName::Eip6110 => Ok(Some(RPCResponse::BlocksByRoot(Arc::new( + SignedBeaconBlock::Eip6110(SignedBeaconBlockEip6110::from_ssz_bytes( + decoded_buffer, + )?), + )))), }, Protocol::BlobsByRange => { Err(RPCError::InvalidData("blobs by range via v2".to_string())) @@ -754,11 +768,13 @@ mod tests { let merge_fork_epoch = Epoch::new(2); let capella_fork_epoch = Epoch::new(3); let deneb_fork_epoch = Epoch::new(4); + let eip6110_fork_epoch = Epoch::new(5); chain_spec.altair_fork_epoch = Some(altair_fork_epoch); chain_spec.bellatrix_fork_epoch = Some(merge_fork_epoch); chain_spec.capella_fork_epoch = Some(capella_fork_epoch); chain_spec.deneb_fork_epoch = Some(deneb_fork_epoch); + chain_spec.eip6110_fork_epoch = Some(eip6110_fork_epoch); let current_slot = match fork_name { ForkName::Base => Slot::new(0), @@ -766,6 +782,7 @@ mod tests { ForkName::Merge => merge_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Capella => capella_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Deneb => deneb_fork_epoch.start_slot(Spec::slots_per_epoch()), + ForkName::Eip6110 => eip6110_fork_epoch.start_slot(Spec::slots_per_epoch()), }; ForkContext::new::(current_slot, Hash256::zero(), &chain_spec) } diff --git a/beacon_node/lighthouse_network/src/rpc/protocol.rs b/beacon_node/lighthouse_network/src/rpc/protocol.rs index ea6293cf9b2..85033b65399 100644 --- a/beacon_node/lighthouse_network/src/rpc/protocol.rs +++ b/beacon_node/lighthouse_network/src/rpc/protocol.rs @@ -89,6 +89,12 @@ lazy_static! { + (::ssz_fixed_len() * ::max_blobs_per_block()) + ssz::BYTES_PER_LENGTH_OFFSET; // Length offset for the blob commitments field. + pub static ref SIGNED_BEACON_BLOCK_EIP6110_MAX: usize = *SIGNED_BEACON_BLOCK_CAPELLA_MAX_WITHOUT_PAYLOAD + + types::ExecutionPayload::::max_execution_payload_eip6110_size() // adding max size of execution payload (~16gb) + + ssz::BYTES_PER_LENGTH_OFFSET // Adding the additional offsets for the `ExecutionPayload` + + (::ssz_fixed_len() * ::max_blobs_per_block()) + + ssz::BYTES_PER_LENGTH_OFFSET; // Length offset for the blob commitments field. + pub static ref BLOCKS_BY_ROOT_REQUEST_MIN: usize = VariableList::::from(Vec::::new()) .as_ssz_bytes() @@ -129,6 +135,7 @@ pub(crate) const MAX_RPC_SIZE_POST_MERGE: usize = 10 * 1_048_576; // 10M pub(crate) const MAX_RPC_SIZE_POST_CAPELLA: usize = 10 * 1_048_576; // 10M // FIXME(sean) should this be increased to account for blobs? pub(crate) const MAX_RPC_SIZE_POST_DENEB: usize = 10 * 1_048_576; // 10M +pub(crate) const MAX_RPC_SIZE_POST_EIP6110: usize = 10 * 1_048_576; // 10M /// The protocol prefix the RPC protocol id. const PROTOCOL_PREFIX: &str = "/eth2/beacon_chain/req"; /// Time allowed for the first byte of a request to arrive before we time out (Time To First Byte). @@ -144,6 +151,7 @@ pub fn max_rpc_size(fork_context: &ForkContext) -> usize { ForkName::Merge => MAX_RPC_SIZE_POST_MERGE, ForkName::Capella => MAX_RPC_SIZE_POST_CAPELLA, ForkName::Deneb => MAX_RPC_SIZE_POST_DENEB, + ForkName::Eip6110 => MAX_RPC_SIZE_POST_EIP6110, } } @@ -172,6 +180,10 @@ pub fn rpc_block_limits_by_fork(current_fork: ForkName) -> RpcLimits { *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and merge blocks *SIGNED_BEACON_BLOCK_DENEB_MAX, // EIP 4844 block is larger than all prior fork blocks ), + ForkName::Eip6110 => RpcLimits::new( + *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and merge blocks + *SIGNED_BEACON_BLOCK_EIP6110_MAX, // EIP 6110 block is larger than all prior fork blocks + ), } } diff --git a/beacon_node/lighthouse_network/src/service/gossip_cache.rs b/beacon_node/lighthouse_network/src/service/gossip_cache.rs index a6e003934cd..449f78b467c 100644 --- a/beacon_node/lighthouse_network/src/service/gossip_cache.rs +++ b/beacon_node/lighthouse_network/src/service/gossip_cache.rs @@ -204,6 +204,7 @@ impl GossipCache { GossipKind::BlsToExecutionChange => self.bls_to_execution_change, GossipKind::LightClientFinalityUpdate => self.light_client_finality_update, GossipKind::LightClientOptimisticUpdate => self.light_client_optimistic_update, + GossipKind::Eip6110 => None, }; let expire_timeout = match expire_timeout { Some(expire_timeout) => expire_timeout, diff --git a/beacon_node/lighthouse_network/src/types/pubsub.rs b/beacon_node/lighthouse_network/src/types/pubsub.rs index c69610cdb0b..a579da7e423 100644 --- a/beacon_node/lighthouse_network/src/types/pubsub.rs +++ b/beacon_node/lighthouse_network/src/types/pubsub.rs @@ -12,8 +12,9 @@ use types::{ Attestation, AttesterSlashing, EthSpec, ForkContext, ForkName, LightClientFinalityUpdate, LightClientOptimisticUpdate, ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockCapella, - SignedBeaconBlockDeneb, SignedBeaconBlockMerge, SignedBlobSidecar, SignedBlsToExecutionChange, - SignedContributionAndProof, SignedVoluntaryExit, SubnetId, SyncCommitteeMessage, SyncSubnetId, + SignedBeaconBlockDeneb, SignedBeaconBlockEip6110, SignedBeaconBlockMerge, SignedBlobSidecar, + SignedBlsToExecutionChange, SignedContributionAndProof, SignedVoluntaryExit, SubnetId, + SyncCommitteeMessage, SyncSubnetId, }; #[derive(Debug, Clone, PartialEq)] @@ -192,6 +193,10 @@ impl PubsubMessage { SignedBeaconBlockDeneb::from_ssz_bytes(data) .map_err(|e| format!("{:?}", e))?, ), + Some(ForkName::Eip6110) => SignedBeaconBlock::::Eip6110( + SignedBeaconBlockEip6110::from_ssz_bytes(data) + .map_err(|e| format!("{:?}", e))?, + ), None => { return Err(format!( "Unknown gossipsub fork digest: {:?}", @@ -203,7 +208,7 @@ impl PubsubMessage { } GossipKind::BlobSidecar(blob_index) => { match fork_context.from_context_bytes(gossip_topic.fork_digest) { - Some(ForkName::Deneb) => { + Some(ForkName::Deneb) | Some(ForkName::Eip6110) => { let blob_sidecar = SignedBlobSidecar::from_ssz_bytes(data) .map_err(|e| format!("{:?}", e))?; Ok(PubsubMessage::BlobSidecar(Box::new(( @@ -277,6 +282,13 @@ impl PubsubMessage { light_client_optimistic_update, ))) } + GossipKind::Eip6110 => { + let eip6110 = SignedBeaconBlockEip6110::from_ssz_bytes(data) + .map_err(|e| format!("{:?}", e))?; + Ok(PubsubMessage::BeaconBlock(Arc::new( + SignedBeaconBlock::Eip6110(eip6110), + ))) + } } } } diff --git a/beacon_node/lighthouse_network/src/types/topics.rs b/beacon_node/lighthouse_network/src/types/topics.rs index db8894035c8..913b7b76c09 100644 --- a/beacon_node/lighthouse_network/src/types/topics.rs +++ b/beacon_node/lighthouse_network/src/types/topics.rs @@ -22,6 +22,7 @@ pub const SYNC_COMMITTEE_PREFIX_TOPIC: &str = "sync_committee_"; pub const BLS_TO_EXECUTION_CHANGE_TOPIC: &str = "bls_to_execution_change"; pub const LIGHT_CLIENT_FINALITY_UPDATE: &str = "light_client_finality_update"; pub const LIGHT_CLIENT_OPTIMISTIC_UPDATE: &str = "light_client_optimistic_update"; +pub const EIP_6110_TOPIC: &str = "eip_6110"; pub const BASE_CORE_TOPICS: [GossipKind; 5] = [ GossipKind::BeaconBlock, @@ -59,6 +60,7 @@ pub fn fork_core_topics(fork_name: &ForkName) -> Vec { deneb_topics.append(&mut deneb_blob_topics); deneb_topics } + ForkName::Eip6110 => vec![GossipKind::Eip6110], } } @@ -117,6 +119,8 @@ pub enum GossipKind { LightClientFinalityUpdate, /// Topic for publishing optimistic updates for light clients. LightClientOptimisticUpdate, + /// Topic for publishing EIP-6110 messages. + Eip6110, } impl std::fmt::Display for GossipKind { @@ -257,6 +261,7 @@ impl std::fmt::Display for GossipTopic { GossipKind::BlsToExecutionChange => BLS_TO_EXECUTION_CHANGE_TOPIC.into(), GossipKind::LightClientFinalityUpdate => LIGHT_CLIENT_FINALITY_UPDATE.into(), GossipKind::LightClientOptimisticUpdate => LIGHT_CLIENT_OPTIMISTIC_UPDATE.into(), + GossipKind::Eip6110 => EIP_6110_TOPIC.into(), }; write!( f, diff --git a/beacon_node/lighthouse_network/tests/common.rs b/beacon_node/lighthouse_network/tests/common.rs index fbcd7f604a7..a057a15899f 100644 --- a/beacon_node/lighthouse_network/tests/common.rs +++ b/beacon_node/lighthouse_network/tests/common.rs @@ -27,11 +27,13 @@ pub fn fork_context(fork_name: ForkName) -> ForkContext { let merge_fork_epoch = Epoch::new(2); let capella_fork_epoch = Epoch::new(3); let deneb_fork_epoch = Epoch::new(4); + let eip6110_fork_epoch = Epoch::new(5); chain_spec.altair_fork_epoch = Some(altair_fork_epoch); chain_spec.bellatrix_fork_epoch = Some(merge_fork_epoch); chain_spec.capella_fork_epoch = Some(capella_fork_epoch); chain_spec.deneb_fork_epoch = Some(deneb_fork_epoch); + chain_spec.eip6110_fork_epoch = Some(eip6110_fork_epoch); let current_slot = match fork_name { ForkName::Base => Slot::new(0), @@ -39,6 +41,7 @@ pub fn fork_context(fork_name: ForkName) -> ForkContext { ForkName::Merge => merge_fork_epoch.start_slot(E::slots_per_epoch()), ForkName::Capella => capella_fork_epoch.start_slot(E::slots_per_epoch()), ForkName::Deneb => deneb_fork_epoch.start_slot(E::slots_per_epoch()), + ForkName::Eip6110 => eip6110_fork_epoch.start_slot(E::slots_per_epoch()), }; ForkContext::new::(current_slot, Hash256::zero(), &chain_spec) } diff --git a/beacon_node/network/src/sync/network_context.rs b/beacon_node/network/src/sync/network_context.rs index ced6aeb52ed..82d564873bc 100644 --- a/beacon_node/network/src/sync/network_context.rs +++ b/beacon_node/network/src/sync/network_context.rs @@ -409,40 +409,26 @@ impl SyncNetworkContext { } } - /// Sends a blocks by root request for a single block lookup. + /// Sends a blocks by root request for a parent request. pub fn single_block_lookup_request( &mut self, peer_id: PeerId, request: BlocksByRootRequest, ) -> Result { - let request = if self - .chain - .is_data_availability_check_required() - .map_err(|_| "Unable to read slot clock")? - { - trace!( - self.log, - "Sending BlobsByRoot Request"; - "method" => "BlobsByRoot", - "count" => request.block_roots.len(), - "peer" => %peer_id - ); - unimplemented!("There is no longer such thing as a single block lookup, since we nede to ask for blobs and blocks separetely"); - } else { - trace!( - self.log, - "Sending BlocksByRoot Request"; - "method" => "BlocksByRoot", - "count" => request.block_roots.len(), - "peer" => %peer_id - ); - Request::BlocksByRoot(request) - }; let id = self.next_id(); let request_id = RequestId::Sync(SyncRequestId::SingleBlock { id }); + + trace!( + self.log, + "Sending BlocksByRoot Request"; + "method" => "BlocksByRoot", + "count" => request.block_roots.len(), + "peer" => %peer_id + ); + self.send_network_msg(NetworkMessage::SendRequest { peer_id, - request, + request: Request::BlocksByRoot(request), request_id, })?; Ok(id) diff --git a/beacon_node/store/src/impls/execution_payload.rs b/beacon_node/store/src/impls/execution_payload.rs index 6445dad3886..29c435aaa95 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::{ BlobSidecarList, EthSpec, ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, - ExecutionPayloadMerge, + ExecutionPayloadEip6110, ExecutionPayloadMerge, }; macro_rules! impl_store_item { @@ -25,6 +25,7 @@ macro_rules! impl_store_item { impl_store_item!(ExecutionPayloadMerge); impl_store_item!(ExecutionPayloadCapella); impl_store_item!(ExecutionPayloadDeneb); +impl_store_item!(ExecutionPayloadEip6110); impl_store_item!(BlobSidecarList); /// This fork-agnostic implementation should be only used for writing. diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 5f864c44a3d..98b24d0f799 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -15,7 +15,7 @@ use types::*; /// /// Utilises lazy-loading from separate storage for its vector fields. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb), + variants(Base, Altair, Merge, Capella, Deneb, Eip6110), variant_attributes(derive(Debug, PartialEq, Clone, Encode, Decode)) )] #[derive(Debug, PartialEq, Clone, Encode)] @@ -67,9 +67,9 @@ where pub current_epoch_attestations: VariableList, T::MaxPendingAttestations>, // Participation (Altair and later) - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub previous_epoch_participation: VariableList, - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub current_epoch_participation: VariableList, // Finality @@ -79,13 +79,13 @@ where pub finalized_checkpoint: Checkpoint, // Inactivity - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub inactivity_scores: VariableList, // Light-client sync committees - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub next_sync_committee: Arc>, // Execution @@ -104,16 +104,24 @@ where partial_getter(rename = "latest_execution_payload_header_deneb") )] pub latest_execution_payload_header: ExecutionPayloadHeaderDeneb, + #[superstruct( + only(Eip6110), + partial_getter(rename = "latest_execution_payload_header_eip6110") + )] + pub latest_execution_payload_header: ExecutionPayloadHeaderEip6110, // Capella - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Capella, Deneb, Eip6110))] pub next_withdrawal_index: u64, - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Capella, Deneb, Eip6110))] pub next_withdrawal_validator_index: u64, #[ssz(skip_serializing, skip_deserializing)] - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Capella, Deneb, Eip6110))] pub historical_summaries: Option>, + + #[superstruct(only(Eip6110))] + pub deposit_receipts_start_index: u64, } /// Implement the conversion function from BeaconState -> PartialBeaconState. @@ -244,6 +252,24 @@ impl PartialBeaconState { ], [historical_summaries] ), + BeaconState::Eip6110(s) => impl_from_state_forgetful!( + s, + outer, + Eip6110, + PartialBeaconStateEip6110, + [ + 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_receipts_start_index + ], + [historical_summaries] + ), } } @@ -488,6 +514,23 @@ impl TryInto> for PartialBeaconState { ], [historical_summaries] ), + PartialBeaconState::Eip6110(inner) => impl_try_into_beacon_state!( + inner, + Eip6110, + BeaconStateEip6110, + [ + 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_receipts_start_index + ], + [historical_summaries] + ), }; Ok(state) } diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index bee9b6f1398..feead9d0b51 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -961,9 +961,11 @@ impl ForkVersionDeserialize for SsePayloadAttributes { ForkName::Merge => serde_json::from_value(value) .map(Self::V1) .map_err(serde::de::Error::custom), - ForkName::Capella | ForkName::Deneb => serde_json::from_value(value) - .map(Self::V2) - .map_err(serde::de::Error::custom), + ForkName::Capella | ForkName::Deneb | ForkName::Eip6110 => { + serde_json::from_value(value) + .map(Self::V2) + .map_err(serde::de::Error::custom) + } ForkName::Base | ForkName::Altair => Err(serde::de::Error::custom(format!( "SsePayloadAttributes deserialization for {fork_name} not implemented" ))), @@ -1315,7 +1317,7 @@ impl> ForkVersionDeserialize D, >(value, fork_name)?)) } - ForkName::Deneb => Ok(BlockContents::BlockAndBlobSidecars( + ForkName::Deneb | ForkName::Eip6110 => Ok(BlockContents::BlockAndBlobSidecars( BeaconBlockAndBlobSidecars::deserialize_by_fork::<'de, D>(value, fork_name)?, )), } @@ -1379,6 +1381,7 @@ impl> From SignedBlockContents::Block(block), //TODO: error handling, this should be try from SignedBeaconBlock::Deneb(_block) => todo!(), + SignedBeaconBlock::Eip6110(_block) => todo!(), } } } 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 787f2e0c323..1b274a851f0 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 @@ -42,6 +42,9 @@ CAPELLA_FORK_EPOCH: 194048 # April 12, 2023, 10:27:35pm UTC # Deneb DENEB_FORK_VERSION: 0x04000000 DENEB_FORK_EPOCH: 18446744073709551615 +# Eip6110 +EIP6110_FORK_VERSION: 0x05000000 +EIP6110_FORK_EPOCH: 18446744073709551615 # Sharding SHARDING_FORK_VERSION: 0x03000000 SHARDING_FORK_EPOCH: 18446744073709551615 diff --git a/consensus/fork_choice/src/fork_choice.rs b/consensus/fork_choice/src/fork_choice.rs index b78e486d512..57b7a3b35c5 100644 --- a/consensus/fork_choice/src/fork_choice.rs +++ b/consensus/fork_choice/src/fork_choice.rs @@ -758,7 +758,8 @@ where let justification_and_finalization_state = match block { // TODO(deneb): Ensure that the final specification // does not substantially modify per epoch processing. - BeaconBlockRef::Deneb(_) + BeaconBlockRef::Eip6110(_) + | BeaconBlockRef::Deneb(_) | BeaconBlockRef::Capella(_) | BeaconBlockRef::Merge(_) | BeaconBlockRef::Altair(_) => { diff --git a/consensus/state_processing/src/common/slash_validator.rs b/consensus/state_processing/src/common/slash_validator.rs index 94148ff6cfc..5674596cd94 100644 --- a/consensus/state_processing/src/common/slash_validator.rs +++ b/consensus/state_processing/src/common/slash_validator.rs @@ -53,7 +53,8 @@ pub fn slash_validator( BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => whistleblower_reward + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => whistleblower_reward .safe_mul(PROPOSER_WEIGHT)? .safe_div(WEIGHT_DENOMINATOR)?, }; diff --git a/consensus/state_processing/src/genesis.rs b/consensus/state_processing/src/genesis.rs index 244cdc588b3..83fe7861674 100644 --- a/consensus/state_processing/src/genesis.rs +++ b/consensus/state_processing/src/genesis.rs @@ -2,15 +2,16 @@ use super::per_block_processing::{ errors::BlockProcessingError, process_operations::process_deposit, }; use crate::common::DepositDataTree; +use crate::per_block_processing::UNSET_DEPOSIT_RECEIPTS_START_INDEX; use crate::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, + upgrade_to_eip6110, }; use safe_arith::{ArithError, SafeArith}; use tree_hash::TreeHash; use types::DEPOSIT_TREE_DEPTH; use types::*; -/// Initialize a `BeaconState` from genesis data. pub fn initialize_beacon_state_from_eth1( eth1_block_hash: Hash256, eth1_timestamp: u64, @@ -20,7 +21,6 @@ pub fn initialize_beacon_state_from_eth1( ) -> Result, BlockProcessingError> { let genesis_time = eth2_genesis_time(eth1_timestamp, spec)?; let eth1_data = Eth1Data { - // Temporary deposit root deposit_root: Hash256::zero(), deposit_count: deposits.len() as u64, block_hash: eth1_block_hash, @@ -42,13 +42,19 @@ pub fn initialize_beacon_state_from_eth1( process_activations(&mut state, spec)?; - // To support testnets with Altair enabled from genesis, perform a possible state upgrade here. - // This must happen *after* deposits and activations are processed or the calculation of sync - // committees during the upgrade will fail. It's a bit cheeky to do this instead of having - // separate Altair genesis initialization logic, but it turns out that our - // use of `BeaconBlock::empty` in `BeaconState::new` is sufficient to correctly initialise - // the `latest_block_header` as per: - // https://github.com/ethereum/eth2.0-specs/pull/2323 + // Now that we have our validators, initialize the caches (including the committees) + state.build_all_caches(spec)?; + + // Set genesis validators root for domain separation and chain versioning + *state.genesis_validators_root_mut() = state.update_validators_tree_hash_cache()?; + + // Set fork version to EIP6110 + state.fork_mut().previous_version = spec.eip6110_fork_version; + state.fork_mut().current_version = spec.eip6110_fork_version; + + // Add deposit_receipts_start_index field with the value UNSET_DEPOSIT_RECEIPTS_START_INDEX + *state.deposit_receipts_start_index_mut()? = UNSET_DEPOSIT_RECEIPTS_START_INDEX; + if spec .altair_fork_epoch .map_or(false, |fork_epoch| fork_epoch == T::genesis_epoch()) @@ -105,16 +111,27 @@ pub fn initialize_beacon_state_from_eth1( // Override latest execution payload header. // See https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#testing - if let Some(ExecutionPayloadHeader::Deneb(header)) = execution_payload_header { - *state.latest_execution_payload_header_deneb_mut()? = header; + if let Some(ExecutionPayloadHeader::Deneb(ref header)) = execution_payload_header { + *state.latest_execution_payload_header_deneb_mut()? = header.clone(); } } - // Now that we have our validators, initialize the caches (including the committees) - state.build_all_caches(spec)?; + // Upgrade to eip6110 if configured from genesis + if spec + .eip6110_fork_epoch + .map_or(false, |fork_epoch| fork_epoch == T::genesis_epoch()) + { + upgrade_to_eip6110(&mut state, spec)?; - // Set genesis validators root for domain separation and chain versioning - *state.genesis_validators_root_mut() = state.update_validators_tree_hash_cache()?; + // Remove intermediate Eip4844 fork from `state.fork`. + state.fork_mut().previous_version = spec.eip6110_fork_version; + + // Override latest execution payload header. + // See https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip6110/beacon-chain.md#testing + if let Some(ExecutionPayloadHeader::Eip6110(header)) = execution_payload_header { + *state.latest_execution_payload_header_eip6110_mut()? = header; + } + } Ok(state) } diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index 53bfbe30692..ec2bdee4faa 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -1,21 +1,20 @@ -use crate::consensus_context::ConsensusContext; -use errors::{BlockOperationError, BlockProcessingError, HeaderInvalid}; -use rayon::prelude::*; -use safe_arith::{ArithError, SafeArith}; -use signature_sets::{block_proposal_signature_set, get_pubkey_from_state, randao_signature_set}; -use std::borrow::Cow; -use tree_hash::TreeHash; -use types::*; - pub use self::verify_attester_slashing::{ get_slashable_indices, get_slashable_indices_modular, verify_attester_slashing, }; pub use self::verify_proposer_slashing::verify_proposer_slashing; +use crate::consensus_context::ConsensusContext; pub use altair::sync_committee::process_sync_aggregate; pub use block_signature_verifier::{BlockSignatureVerifier, ParallelSignatureSets}; pub use deneb::deneb::process_blob_kzg_commitments; +use errors::{BlockOperationError, BlockProcessingError, HeaderInvalid}; pub use is_valid_indexed_attestation::is_valid_indexed_attestation; pub use process_operations::process_operations; +use rayon::prelude::*; +use safe_arith::{ArithError, SafeArith}; +use signature_sets::{block_proposal_signature_set, get_pubkey_from_state, randao_signature_set}; +use std::borrow::Cow; +use tree_hash::TreeHash; +use types::*; pub use verify_attestation::{ verify_attestation_for_block_inclusion, verify_attestation_for_state, }; @@ -40,7 +39,7 @@ mod verify_deposit; mod verify_exit; mod verify_proposer_slashing; -use crate::common::decrease_balance; +use crate::common::{decrease_balance, increase_balance}; use crate::StateProcessingStrategy; #[cfg(feature = "arbitrary-fuzz")] @@ -415,6 +414,87 @@ pub fn process_execution_payload>( _ => return Err(BlockProcessingError::IncorrectStateType), } } + ExecutionPayloadHeaderRefMut::Eip6110(header_mut) => { + match payload.to_execution_payload_header() { + ExecutionPayloadHeader::Eip6110(header) => *header_mut = header, + _ => return Err(BlockProcessingError::IncorrectStateType), + } + } + } + + Ok(()) +} + +pub const UNSET_DEPOSIT_RECEIPTS_START_INDEX: u64 = std::u64::MAX; +pub fn process_deposit_receipt( + state: &mut BeaconState, + deposit_receipt: &DepositReceipt, + spec: &ChainSpec, +) -> Result<(), BlockProcessingError> { + match state { + BeaconState::Eip6110(_) => { + let start_index = state + .deposit_receipts_start_index() + .map_err(|_| BlockProcessingError::DepositReceiptError)?; + + if start_index == UNSET_DEPOSIT_RECEIPTS_START_INDEX { + *state + .deposit_receipts_start_index_mut() + .map_err(|_| BlockProcessingError::DepositReceiptError)? = + deposit_receipt.index; + } + + // `apply_deposit` logic from `process_deposit` function: + let amount = deposit_receipt.amount; + + if let Ok(Some(index)) = get_existing_validator_index(state, &deposit_receipt.pubkey) { + increase_balance(state, index as usize, amount)?; + } else { + let deposit_data = DepositData { + pubkey: deposit_receipt.pubkey, + withdrawal_credentials: deposit_receipt.withdrawal_credentials, + amount: deposit_receipt.amount, + signature: deposit_receipt.signature.clone(), + }; + + if verify_deposit_signature(&deposit_data, spec).is_err() { + return Ok(()); + } + + let remainder = amount.safe_rem(spec.effective_balance_increment)?; + let effective_balance = + std::cmp::min(amount.safe_sub(remainder)?, spec.max_effective_balance); + + let validator = Validator { + pubkey: deposit_receipt.pubkey, + withdrawal_credentials: deposit_receipt.withdrawal_credentials, + activation_eligibility_epoch: spec.far_future_epoch, + activation_epoch: spec.far_future_epoch, + exit_epoch: spec.far_future_epoch, + withdrawable_epoch: spec.far_future_epoch, + effective_balance, + slashed: false, + }; + state.validators_mut().push(validator)?; + state.balances_mut().push(deposit_receipt.amount)?; + + if let Ok(previous_epoch_participation) = state.previous_epoch_participation_mut() { + previous_epoch_participation.push(ParticipationFlags::default())?; + } + if let Ok(current_epoch_participation) = state.current_epoch_participation_mut() { + current_epoch_participation.push(ParticipationFlags::default())?; + } + if let Ok(inactivity_scores) = state.inactivity_scores_mut() { + inactivity_scores.push(0)?; + } + } + } + + BeaconState::Base(_) + | BeaconState::Altair(_) + | BeaconState::Merge(_) + | BeaconState::Capella(_) + | BeaconState::Deneb(_) => {} } Ok(()) @@ -527,7 +607,7 @@ pub fn process_withdrawals>( ) -> Result<(), BlockProcessingError> { match state { BeaconState::Merge(_) => Ok(()), - BeaconState::Capella(_) | BeaconState::Deneb(_) => { + BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Eip6110(_) => { let expected_withdrawals = get_expected_withdrawals(state, spec)?; let expected_root = expected_withdrawals.tree_hash_root(); let withdrawals_root = payload.withdrawals_root()?; diff --git a/consensus/state_processing/src/per_block_processing/errors.rs b/consensus/state_processing/src/per_block_processing/errors.rs index 5c34afd593e..0370945a16a 100644 --- a/consensus/state_processing/src/per_block_processing/errors.rs +++ b/consensus/state_processing/src/per_block_processing/errors.rs @@ -95,6 +95,7 @@ pub enum BlockProcessingError { length: usize, }, WithdrawalCredentialsInvalid, + DepositReceiptError, } impl From for BlockProcessingError { diff --git a/consensus/state_processing/src/per_block_processing/process_operations.rs b/consensus/state_processing/src/per_block_processing/process_operations.rs index 091f961dbd1..9480be7ef8e 100644 --- a/consensus/state_processing/src/per_block_processing/process_operations.rs +++ b/consensus/state_processing/src/per_block_processing/process_operations.rs @@ -16,6 +16,30 @@ pub fn process_operations>( ctxt: &mut ConsensusContext, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { + let eth1_deposit_index_limit = std::cmp::min( + state.eth1_data().deposit_count, + state + .deposit_receipts_start_index() + .unwrap_or_else(|_| state.eth1_data().deposit_count), + ); + + let expected_deposit_count = if state.eth1_deposit_index() < eth1_deposit_index_limit { + let diff = eth1_deposit_index_limit + .checked_sub(state.eth1_deposit_index()) + .ok_or(BlockProcessingError::DepositReceiptError)?; + + std::cmp::min( + ::MaxDeposits::to_u64() as usize, + diff as usize, + ) as u64 + } else { + 0 + }; + + if block_body.deposits().len() as u64 != expected_deposit_count { + return Err(BlockProcessingError::DepositReceiptError); + } + process_proposer_slashings( state, block_body.proposer_slashings(), @@ -31,13 +55,25 @@ pub fn process_operations>( spec, )?; process_attestations(state, block_body, verify_signatures, ctxt, spec)?; - process_deposits(state, block_body.deposits(), spec)?; process_exits(state, block_body.voluntary_exits(), verify_signatures, spec)?; if let Ok(bls_to_execution_changes) = block_body.bls_to_execution_changes() { process_bls_to_execution_changes(state, bls_to_execution_changes, verify_signatures, spec)?; } + if let Ok(payload) = block_body.execution_payload() { + if let Ok(deposit_receipts) = payload.deposit_receipts() { + for deposit_receipt in deposit_receipts.iter() { + process_deposit_receipt(state, deposit_receipt, spec)?; + } + } + } + + // Only process deposits if expected_deposit_count is not zero. + if expected_deposit_count > 0 { + process_deposits(state, block_body.deposits(), spec)?; + } + Ok(()) } @@ -257,7 +293,8 @@ pub fn process_attestations>( BeaconBlockBodyRef::Altair(_) | BeaconBlockBodyRef::Merge(_) | BeaconBlockBodyRef::Capella(_) - | BeaconBlockBodyRef::Deneb(_) => { + | BeaconBlockBodyRef::Deneb(_) + | BeaconBlockBodyRef::Eip6110(_) => { altair::process_attestations( state, block_body.attestations(), diff --git a/consensus/state_processing/src/per_epoch_processing.rs b/consensus/state_processing/src/per_epoch_processing.rs index d5d06037cd8..8333d617fb7 100644 --- a/consensus/state_processing/src/per_epoch_processing.rs +++ b/consensus/state_processing/src/per_epoch_processing.rs @@ -40,7 +40,9 @@ pub fn process_epoch( match state { BeaconState::Base(_) => base::process_epoch(state, spec), BeaconState::Altair(_) | BeaconState::Merge(_) => altair::process_epoch(state, spec), - BeaconState::Capella(_) | BeaconState::Deneb(_) => capella::process_epoch(state, spec), + BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Eip6110(_) => { + capella::process_epoch(state, spec) + } } } diff --git a/consensus/state_processing/src/per_slot_processing.rs b/consensus/state_processing/src/per_slot_processing.rs index 75b8c6b0061..4446b5d9890 100644 --- a/consensus/state_processing/src/per_slot_processing.rs +++ b/consensus/state_processing/src/per_slot_processing.rs @@ -1,5 +1,6 @@ use crate::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, + upgrade_to_eip6110, }; use crate::{per_epoch_processing::EpochProcessingSummary, *}; use safe_arith::{ArithError, SafeArith}; @@ -65,6 +66,10 @@ pub fn per_slot_processing( if spec.deneb_fork_epoch == Some(state.current_epoch()) { upgrade_to_deneb(state, spec)?; } + // Eip6110 + if spec.eip6110_fork_epoch == Some(state.current_epoch()) { + upgrade_to_eip6110(state, spec)?; + } } Ok(summary) diff --git a/consensus/state_processing/src/upgrade.rs b/consensus/state_processing/src/upgrade.rs index 1509ee0e50f..433ce0ccd31 100644 --- a/consensus/state_processing/src/upgrade.rs +++ b/consensus/state_processing/src/upgrade.rs @@ -1,9 +1,11 @@ pub mod altair; pub mod capella; pub mod deneb; +pub mod eip6110; pub mod merge; pub use altair::upgrade_to_altair; pub use capella::upgrade_to_capella; pub use deneb::upgrade_to_deneb; +pub use eip6110::upgrade_to_eip6110; pub use merge::upgrade_to_bellatrix; diff --git a/consensus/state_processing/src/upgrade/eip6110.rs b/consensus/state_processing/src/upgrade/eip6110.rs new file mode 100644 index 00000000000..68b25209fc3 --- /dev/null +++ b/consensus/state_processing/src/upgrade/eip6110.rs @@ -0,0 +1,72 @@ +use std::mem; +use types::{BeaconState, BeaconStateEip6110, BeaconStateError as Error, ChainSpec, EthSpec, Fork}; + +/// Transform a `Eip4844` state into an `Eip6110` state. +pub fn upgrade_to_eip6110( + pre_state: &mut BeaconState, + spec: &ChainSpec, +) -> Result<(), Error> { + let epoch = pre_state.current_epoch(); + let pre = pre_state.as_deneb_mut()?; + + let previous_fork_version = pre.fork.current_version; + + let post = BeaconState::Eip6110(BeaconStateEip6110 { + // Versioning + genesis_time: pre.genesis_time, + genesis_validators_root: pre.genesis_validators_root, + slot: pre.slot, + fork: Fork { + previous_version: previous_fork_version, + current_version: spec.eip6110_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_eip6110(), + // Capella + next_withdrawal_index: pre.next_withdrawal_index, + next_withdrawal_validator_index: pre.next_withdrawal_validator_index, + historical_summaries: pre.historical_summaries.clone(), + // Caches + total_active_balance: pre.total_active_balance, + committee_caches: mem::take(&mut pre.committee_caches), + pubkey_cache: mem::take(&mut pre.pubkey_cache), + exit_cache: mem::take(&mut pre.exit_cache), + tree_hash_cache: mem::take(&mut pre.tree_hash_cache), + // Eip6110 + deposit_receipts_start_index: 0, + }); + + *pre_state = post; + + Ok(()) +} diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index 0d52d708012..a05cb82ba43 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -1,6 +1,6 @@ use crate::beacon_block_body::{ - BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyDeneb, BeaconBlockBodyMerge, - BeaconBlockBodyRef, BeaconBlockBodyRefMut, + BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyDeneb, BeaconBlockBodyEip6110, + BeaconBlockBodyMerge, BeaconBlockBodyRef, BeaconBlockBodyRefMut, }; use crate::test_utils::TestRandom; use crate::*; @@ -17,7 +17,7 @@ use tree_hash_derive::TreeHash; /// A block of the `BeaconChain`. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb), + variants(Base, Altair, Merge, Capella, Deneb, Eip6110), variant_attributes( derive( Debug, @@ -74,6 +74,8 @@ pub struct BeaconBlock = FullPayload pub body: BeaconBlockBodyCapella, #[superstruct(only(Deneb), partial_getter(rename = "body_deneb"))] pub body: BeaconBlockBodyDeneb, + #[superstruct(only(Eip6110), partial_getter(rename = "body_eip6110"))] + pub body: BeaconBlockBodyEip6110, } pub type BlindedBeaconBlock = BeaconBlock>; @@ -126,8 +128,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 { - BeaconBlockDeneb::from_ssz_bytes(bytes) - .map(BeaconBlock::Deneb) + BeaconBlockEip6110::from_ssz_bytes(bytes) + .map(BeaconBlock::Eip6110) + .or_else(|_| BeaconBlockDeneb::from_ssz_bytes(bytes).map(BeaconBlock::Deneb)) .or_else(|_| BeaconBlockCapella::from_ssz_bytes(bytes).map(BeaconBlock::Capella)) .or_else(|_| BeaconBlockMerge::from_ssz_bytes(bytes).map(BeaconBlock::Merge)) .or_else(|_| BeaconBlockAltair::from_ssz_bytes(bytes).map(BeaconBlock::Altair)) @@ -223,6 +226,7 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload> BeaconBlockRef<'a, T, Payl BeaconBlockRef::Merge { .. } => ForkName::Merge, BeaconBlockRef::Capella { .. } => ForkName::Capella, BeaconBlockRef::Deneb { .. } => ForkName::Deneb, + BeaconBlockRef::Eip6110 { .. } => ForkName::Eip6110, } } @@ -567,6 +571,36 @@ impl> EmptyBlock for BeaconBlockCape } } +impl> EmptyBlock for BeaconBlockEip6110 { + /// Returns an empty Eip6110 block to be used during genesis. + fn empty(spec: &ChainSpec) -> Self { + BeaconBlockEip6110 { + slot: spec.genesis_slot, + proposer_index: 0, + parent_root: Hash256::zero(), + state_root: Hash256::zero(), + body: BeaconBlockBodyEip6110 { + 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::Eip6110::default(), + bls_to_execution_changes: VariableList::empty(), + blob_kzg_commitments: VariableList::empty(), + }, + } + } +} + impl> EmptyBlock for BeaconBlockDeneb { /// Returns an empty Deneb block to be used during genesis. fn empty(spec: &ChainSpec) -> Self { @@ -677,6 +711,7 @@ impl_from!(BeaconBlockAltair, >, >, |body impl_from!(BeaconBlockMerge, >, >, |body: BeaconBlockBodyMerge<_, _>| body.into()); impl_from!(BeaconBlockCapella, >, >, |body: BeaconBlockBodyCapella<_, _>| body.into()); impl_from!(BeaconBlockDeneb, >, >, |body: BeaconBlockBodyDeneb<_, _>| body.into()); +impl_from!(BeaconBlockEip6110, >, >, |body: BeaconBlockBodyEip6110<_, _>| body.into()); // We can clone blocks with payloads to blocks without payloads, without cloning the payload. macro_rules! impl_clone_as_blinded { @@ -709,6 +744,7 @@ impl_clone_as_blinded!(BeaconBlockAltair, >, >, >); impl_clone_as_blinded!(BeaconBlockCapella, >, >); impl_clone_as_blinded!(BeaconBlockDeneb, >, >); +impl_clone_as_blinded!(BeaconBlockEip6110, >, >); // A reference to a full beacon block can be cloned into a blinded beacon block, without cloning the // execution payload. @@ -860,10 +896,13 @@ mod tests { let capella_slot = capella_epoch.start_slot(E::slots_per_epoch()); let deneb_epoch = capella_epoch + 1; let deneb_slot = deneb_epoch.start_slot(E::slots_per_epoch()); + let eip6110_epoch = deneb_epoch + 1; + let eip6110_slot = eip6110_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.eip6110_fork_epoch = Some(eip6110_epoch); // BeaconBlockBase { @@ -952,5 +991,27 @@ mod tests { BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec) .expect_err("bad deneb block cannot be decoded"); } + + // BeaconBlockEip6110 + { + let good_block = BeaconBlock::Eip6110(BeaconBlockEip6110 { + slot: eip6110_slot, + ..<_>::random_for_test(rng) + }); + // It's invalid to have an Eip4844 block with a epoch lower than the fork epoch. + let bad_block = { + let mut bad = good_block.clone(); + *bad.slot_mut() = deneb_slot; + bad + }; + + assert_eq!( + BeaconBlock::from_ssz_bytes(&good_block.as_ssz_bytes(), &spec) + .expect("good eip6110 block can be decoded"), + good_block + ); + BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec) + .expect_err("bad eip6110 block cannot be decoded"); + } } } diff --git a/consensus/types/src/beacon_block_body.rs b/consensus/types/src/beacon_block_body.rs index f2174467b2a..a99d6e20d50 100644 --- a/consensus/types/src/beacon_block_body.rs +++ b/consensus/types/src/beacon_block_body.rs @@ -15,7 +15,7 @@ pub type KzgCommitments = VariableList::MaxBlob /// /// This *superstruct* abstracts over the hard-fork. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb), + variants(Base, Altair, Merge, Capella, Deneb, Eip6110), variant_attributes( derive( Debug, @@ -53,7 +53,7 @@ pub struct BeaconBlockBody = FullPay pub attestations: VariableList, T::MaxAttestations>, pub deposits: VariableList, pub voluntary_exits: VariableList, - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] 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 @@ -67,10 +67,13 @@ pub struct BeaconBlockBody = FullPay #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))] #[serde(flatten)] pub execution_payload: Payload::Deneb, - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Eip6110), partial_getter(rename = "execution_payload_eip6110"))] + #[serde(flatten)] + pub execution_payload: Payload::Eip6110, + #[superstruct(only(Capella, Deneb, Eip6110))] pub bls_to_execution_changes: VariableList, - #[superstruct(only(Deneb))] + #[superstruct(only(Deneb, Eip6110))] pub blob_kzg_commitments: KzgCommitments, #[superstruct(only(Base, Altair))] #[ssz(skip_serializing, skip_deserializing)] @@ -93,6 +96,7 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, T, Self::Merge(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Capella(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Deneb(body) => Ok(Payload::Ref::from(&body.execution_payload)), + Self::Eip6110(body) => Ok(Payload::Ref::from(&body.execution_payload)), } } } @@ -106,6 +110,7 @@ impl<'a, T: EthSpec> BeaconBlockBodyRef<'a, T> { BeaconBlockBodyRef::Merge { .. } => ForkName::Merge, BeaconBlockBodyRef::Capella { .. } => ForkName::Capella, BeaconBlockBodyRef::Deneb { .. } => ForkName::Deneb, + BeaconBlockBodyRef::Eip6110 { .. } => ForkName::Eip6110, } } } @@ -374,6 +379,50 @@ impl From>> } } +impl From>> + for ( + BeaconBlockBodyEip6110>, + Option>, + ) +{ + fn from(body: BeaconBlockBodyEip6110>) -> Self { + let BeaconBlockBodyEip6110 { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadEip6110 { execution_payload }, + bls_to_execution_changes, + blob_kzg_commitments, + } = body; + + ( + BeaconBlockBodyEip6110 { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: BlindedPayloadEip6110 { + execution_payload_header: From::from(&execution_payload), + }, + bls_to_execution_changes, + blob_kzg_commitments, + }, + 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> { @@ -491,6 +540,42 @@ impl BeaconBlockBodyDeneb> { } } +impl BeaconBlockBodyEip6110> { + pub fn clone_as_blinded(&self) -> BeaconBlockBodyEip6110> { + let BeaconBlockBodyEip6110 { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadEip6110 { execution_payload }, + bls_to_execution_changes, + blob_kzg_commitments, + } = self; + + BeaconBlockBodyEip6110 { + 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: BlindedPayloadEip6110 { + execution_payload_header: execution_payload.into(), + }, + bls_to_execution_changes: bls_to_execution_changes.clone(), + blob_kzg_commitments: blob_kzg_commitments.clone(), + } + } +} + impl From>> for ( BeaconBlockBody>, diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index ca333048a79..c90037fbc4d 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -176,7 +176,7 @@ impl From for Hash256 { /// The state of the `BeaconChain` at some slot. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb), + variants(Base, Altair, Merge, Capella, Deneb, Eip6110), variant_attributes( derive( Derivative, @@ -256,9 +256,9 @@ where pub current_epoch_attestations: VariableList, T::MaxPendingAttestations>, // Participation (Altair and later) - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub previous_epoch_participation: VariableList, - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub current_epoch_participation: VariableList, // Finality @@ -273,13 +273,13 @@ where // Inactivity #[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")] - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub inactivity_scores: VariableList, // Light-client sync committees - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Merge, Capella, Deneb))] + #[superstruct(only(Altair, Merge, Capella, Deneb, Eip6110))] pub next_sync_committee: Arc>, // Execution @@ -298,17 +298,25 @@ where partial_getter(rename = "latest_execution_payload_header_deneb") )] pub latest_execution_payload_header: ExecutionPayloadHeaderDeneb, + #[superstruct( + only(Eip6110), + partial_getter(rename = "latest_execution_payload_header_eip6110") + )] + pub latest_execution_payload_header: ExecutionPayloadHeaderEip6110, // Capella - #[superstruct(only(Capella, Deneb), partial_getter(copy))] + #[superstruct(only(Capella, Deneb, Eip6110), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] pub next_withdrawal_index: u64, - #[superstruct(only(Capella, Deneb), partial_getter(copy))] + #[superstruct(only(Capella, Deneb, Eip6110), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] pub next_withdrawal_validator_index: u64, // Deep history valid from Capella onwards. - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Capella, Deneb, Eip6110))] pub historical_summaries: VariableList, + #[superstruct(only(Eip6110), partial_getter(copy))] + #[serde(with = "serde_utils::quoted_u64")] + pub deposit_receipts_start_index: u64, // Caching (not in the spec) #[serde(skip_serializing, skip_deserializing)] @@ -437,6 +445,7 @@ impl BeaconState { BeaconState::Merge { .. } => ForkName::Merge, BeaconState::Capella { .. } => ForkName::Capella, BeaconState::Deneb { .. } => ForkName::Deneb, + BeaconState::Eip6110 { .. } => ForkName::Eip6110, } } @@ -730,6 +739,9 @@ impl BeaconState { BeaconState::Deneb(state) => Ok(ExecutionPayloadHeaderRef::Deneb( &state.latest_execution_payload_header, )), + BeaconState::Eip6110(state) => Ok(ExecutionPayloadHeaderRef::Eip6110( + &state.latest_execution_payload_header, + )), } } @@ -747,6 +759,9 @@ impl BeaconState { BeaconState::Deneb(state) => Ok(ExecutionPayloadHeaderRefMut::Deneb( &mut state.latest_execution_payload_header, )), + BeaconState::Eip6110(state) => Ok(ExecutionPayloadHeaderRefMut::Eip6110( + &mut state.latest_execution_payload_header, + )), } } @@ -1176,6 +1191,7 @@ impl BeaconState { BeaconState::Merge(state) => (&mut state.validators, &mut state.balances), BeaconState::Capella(state) => (&mut state.validators, &mut state.balances), BeaconState::Deneb(state) => (&mut state.validators, &mut state.balances), + BeaconState::Eip6110(state) => (&mut state.validators, &mut state.balances), } } @@ -1374,6 +1390,7 @@ impl BeaconState { BeaconState::Merge(state) => Ok(&mut state.current_epoch_participation), BeaconState::Capella(state) => Ok(&mut state.current_epoch_participation), BeaconState::Deneb(state) => Ok(&mut state.current_epoch_participation), + BeaconState::Eip6110(state) => Ok(&mut state.current_epoch_participation), } } else if epoch == self.previous_epoch() { match self { @@ -1382,6 +1399,7 @@ impl BeaconState { BeaconState::Merge(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Capella(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Deneb(state) => Ok(&mut state.previous_epoch_participation), + BeaconState::Eip6110(state) => Ok(&mut state.previous_epoch_participation), } } else { Err(BeaconStateError::EpochOutOfBounds) @@ -1688,6 +1706,7 @@ impl BeaconState { BeaconState::Merge(inner) => BeaconState::Merge(inner.clone()), BeaconState::Capella(inner) => BeaconState::Capella(inner.clone()), BeaconState::Deneb(inner) => BeaconState::Deneb(inner.clone()), + BeaconState::Eip6110(inner) => BeaconState::Eip6110(inner.clone()), }; if config.committee_caches { *res.committee_caches_mut() = self.committee_caches().clone(); @@ -1857,6 +1876,7 @@ impl CompareFields for BeaconState { (BeaconState::Merge(x), BeaconState::Merge(y)) => x.compare_fields(y), (BeaconState::Capella(x), BeaconState::Capella(y)) => x.compare_fields(y), (BeaconState::Deneb(x), BeaconState::Deneb(y)) => x.compare_fields(y), + (BeaconState::Eip6110(x), BeaconState::Eip6110(y)) => x.compare_fields(y), _ => panic!("compare_fields: mismatched state variants",), } } @@ -1950,6 +1970,9 @@ pub mod ssz_tagged_beacon_state { body, )?)), ForkName::Deneb => Ok(BeaconState::Deneb(BeaconStateDeneb::from_ssz_bytes(body)?)), + ForkName::Eip6110 => Ok(BeaconState::Eip6110(BeaconStateEip6110::from_ssz_bytes( + body, + )?)), } } } diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index e510b0a27e7..91c69f1c32f 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -167,6 +167,12 @@ pub struct ChainSpec { pub deneb_fork_version: [u8; 4], pub deneb_fork_epoch: Option, + /* + * Eip6110 hard fork params + */ + pub eip6110_fork_version: [u8; 4], + pub eip6110_fork_epoch: Option, + /* * Networking */ @@ -253,15 +259,18 @@ 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.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::Merge, - _ => match self.altair_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Altair, - _ => ForkName::Base, + match self.eip6110_fork_epoch { + Some(fork_epoch) if epoch >= fork_epoch => ForkName::Eip6110, + _ => 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::Merge, + _ => match self.altair_fork_epoch { + Some(fork_epoch) if epoch >= fork_epoch => ForkName::Altair, + _ => ForkName::Base, + }, }, }, }, @@ -276,6 +285,7 @@ impl ChainSpec { ForkName::Merge => self.bellatrix_fork_version, ForkName::Capella => self.capella_fork_version, ForkName::Deneb => self.deneb_fork_version, + ForkName::Eip6110 => self.eip6110_fork_version, } } @@ -287,6 +297,7 @@ impl ChainSpec { ForkName::Merge => self.bellatrix_fork_epoch, ForkName::Capella => self.capella_fork_epoch, ForkName::Deneb => self.deneb_fork_epoch, + ForkName::Eip6110 => self.eip6110_fork_epoch, } } @@ -298,6 +309,7 @@ impl ChainSpec { BeaconState::Merge(_) => self.inactivity_penalty_quotient_bellatrix, BeaconState::Capella(_) => self.inactivity_penalty_quotient_bellatrix, BeaconState::Deneb(_) => self.inactivity_penalty_quotient_bellatrix, + BeaconState::Eip6110(_) => self.inactivity_penalty_quotient_bellatrix, } } @@ -312,6 +324,7 @@ impl ChainSpec { BeaconState::Merge(_) => self.proportional_slashing_multiplier_bellatrix, BeaconState::Capella(_) => self.proportional_slashing_multiplier_bellatrix, BeaconState::Deneb(_) => self.proportional_slashing_multiplier_bellatrix, + BeaconState::Eip6110(_) => self.proportional_slashing_multiplier_bellatrix, } } @@ -326,6 +339,7 @@ impl ChainSpec { BeaconState::Merge(_) => self.min_slashing_penalty_quotient_bellatrix, BeaconState::Capella(_) => self.min_slashing_penalty_quotient_bellatrix, BeaconState::Deneb(_) => self.min_slashing_penalty_quotient_bellatrix, + BeaconState::Eip6110(_) => self.min_slashing_penalty_quotient_bellatrix, } } @@ -630,6 +644,12 @@ impl ChainSpec { deneb_fork_version: [0x04, 0x00, 0x00, 0x00], deneb_fork_epoch: None, + /* + * Eip6110 hard fork params + */ + eip6110_fork_version: [0x05, 0x00, 0x00, 0x00], + eip6110_fork_epoch: None, + /* * Network specific */ @@ -698,6 +718,9 @@ impl ChainSpec { // Deneb deneb_fork_version: [0x04, 0x00, 0x00, 0x01], deneb_fork_epoch: None, + // Eip6110 + eip6110_fork_version: [0x05, 0x00, 0x00, 0x01], + eip6110_fork_epoch: None, // Other network_id: 2, // lighthouse testnet network id deposit_chain_id: 5, @@ -865,6 +888,12 @@ impl ChainSpec { deneb_fork_version: [0x04, 0x00, 0x00, 0x64], deneb_fork_epoch: None, + /* + * Eip6110 hard fork params + */ + eip6110_fork_version: [0x05, 0x00, 0x00, 0x64], + eip6110_fork_epoch: None, + /* * Network specific */ @@ -962,6 +991,14 @@ pub struct Config { #[serde(deserialize_with = "deserialize_fork_epoch")] pub deneb_fork_epoch: Option>, + #[serde(default = "default_eip6110_fork_version")] + #[serde(with = "serde_utils::bytes_4_hex")] + eip6110_fork_version: [u8; 4], + #[serde(default)] + #[serde(serialize_with = "serialize_fork_epoch")] + #[serde(deserialize_with = "deserialize_fork_epoch")] + pub eip6110_fork_epoch: Option>, + #[serde(with = "serde_utils::quoted_u64")] seconds_per_slot: u64, #[serde(with = "serde_utils::quoted_u64")] @@ -1012,6 +1049,11 @@ fn default_deneb_fork_version() -> [u8; 4] { [0xff, 0xff, 0xff, 0xff] } +fn default_eip6110_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 @@ -1120,6 +1162,10 @@ impl Config { deneb_fork_epoch: spec .deneb_fork_epoch .map(|epoch| MaybeQuoted { value: epoch }), + eip6110_fork_version: spec.eip6110_fork_version, + eip6110_fork_epoch: spec + .eip6110_fork_epoch + .map(|epoch| MaybeQuoted { value: epoch }), seconds_per_slot: spec.seconds_per_slot, seconds_per_eth1_block: spec.seconds_per_eth1_block, @@ -1170,6 +1216,8 @@ impl Config { capella_fork_version, deneb_fork_epoch, deneb_fork_version, + eip6110_fork_epoch, + eip6110_fork_version, seconds_per_slot, seconds_per_eth1_block, min_validator_withdrawability_delay, @@ -1205,6 +1253,8 @@ impl Config { capella_fork_version, deneb_fork_epoch: deneb_fork_epoch.map(|q| q.value), deneb_fork_version, + eip6110_fork_epoch: eip6110_fork_epoch.map(|q| q.value), + eip6110_fork_version, seconds_per_slot, seconds_per_eth1_block, min_validator_withdrawability_delay, diff --git a/consensus/types/src/eip6110.rs b/consensus/types/src/eip6110.rs new file mode 100644 index 00000000000..c7749d5cc12 --- /dev/null +++ b/consensus/types/src/eip6110.rs @@ -0,0 +1,49 @@ +use crate::test_utils::TestRandom; +use crate::*; +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use std::hash::{Hash, Hasher}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + arbitrary::Arbitrary, Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, +)] +pub struct DepositReceipt { + pub pubkey: PublicKeyBytes, + pub withdrawal_credentials: Hash256, + #[serde(with = "serde_utils::quoted_u64")] + pub amount: u64, + pub signature: SignatureBytes, + #[serde(with = "serde_utils::quoted_u64")] + pub index: u64, +} + +// Manually implement the Eq trait for DepositReceipt +impl Eq for DepositReceipt {} + +impl PartialEq for DepositReceipt { + fn eq(&self, other: &DepositReceipt) -> bool { + self.pubkey == other.pubkey + && self.withdrawal_credentials == other.withdrawal_credentials + && self.amount == other.amount + && self.index == other.index + } +} + +// Manually implement the Hash trait for DepositReceipt +impl Hash for DepositReceipt { + fn hash(&self, state: &mut H) { + self.pubkey.hash(state); + self.withdrawal_credentials.hash(state); + self.amount.hash(state); + self.index.hash(state); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(DepositReceipt); +} diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index e7c00d19573..a1a1a67980d 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -108,6 +108,7 @@ pub trait EthSpec: type MaxBlobsPerBlock: Unsigned + Clone + Sync + Send + Debug + PartialEq; type FieldElementsPerBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq; type BytesPerFieldElement: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type MaxDepositReceiptsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* * Derived values (set these CAREFULLY) */ @@ -251,6 +252,11 @@ pub trait EthSpec: Self::MaxWithdrawalsPerPayload::to_usize() } + /// Returns the `MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD` constant for this specification. + fn max_deposit_receipts_per_payload() -> usize { + Self::MaxDepositReceiptsPerPayload::to_usize() + } + /// Returns the `MAX_BLOBS_PER_BLOCK` constant for this specification. fn max_blobs_per_block() -> usize { Self::MaxBlobsPerBlock::to_usize() @@ -303,6 +309,7 @@ impl EthSpec for MainnetEthSpec { type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch type MaxBlsToExecutionChanges = U16; type MaxWithdrawalsPerPayload = U16; + type MaxDepositReceiptsPerPayload = U8192; fn default_spec() -> ChainSpec { ChainSpec::mainnet() @@ -352,7 +359,8 @@ impl EthSpec for MinimalEthSpec { MaxExtraDataBytes, MaxBlsToExecutionChanges, MaxBlobsPerBlock, - BytesPerFieldElement + BytesPerFieldElement, + MaxDepositReceiptsPerPayload }); fn default_spec() -> ChainSpec { @@ -402,6 +410,7 @@ impl EthSpec for GnosisEthSpec { type FieldElementsPerBlob = U4096; type BytesPerFieldElement = U32; type BytesPerBlob = U131072; + type MaxDepositReceiptsPerPayload = U8192; fn default_spec() -> ChainSpec { ChainSpec::gnosis() diff --git a/consensus/types/src/execution_payload.rs b/consensus/types/src/execution_payload.rs index 448c1bf23ea..2a89b39dd26 100644 --- a/consensus/types/src/execution_payload.rs +++ b/consensus/types/src/execution_payload.rs @@ -14,8 +14,11 @@ pub type Transactions = VariableList< pub type Withdrawals = VariableList::MaxWithdrawalsPerPayload>; +pub type DepositReceipts = + VariableList::MaxDepositReceiptsPerPayload>; + #[superstruct( - variants(Merge, Capella, Deneb), + variants(Merge, Capella, Deneb, Eip6110), variant_attributes( derive( Default, @@ -81,12 +84,14 @@ 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))] + #[superstruct(only(Capella, Deneb, Eip6110))] pub withdrawals: Withdrawals, - #[superstruct(only(Deneb))] + #[superstruct(only(Deneb, Eip6110))] #[serde(with = "serde_utils::quoted_u256")] #[superstruct(getter(copy))] pub excess_data_gas: Uint256, + #[superstruct(only(Eip6110))] + pub deposit_receipts: DepositReceipts, } impl<'a, T: EthSpec> ExecutionPayloadRef<'a, T> { @@ -108,6 +113,7 @@ impl ExecutionPayload { ForkName::Merge => ExecutionPayloadMerge::from_ssz_bytes(bytes).map(Self::Merge), ForkName::Capella => ExecutionPayloadCapella::from_ssz_bytes(bytes).map(Self::Capella), ForkName::Deneb => ExecutionPayloadDeneb::from_ssz_bytes(bytes).map(Self::Deneb), + ForkName::Eip6110 => ExecutionPayloadEip6110::from_ssz_bytes(bytes).map(Self::Eip6110), } } @@ -147,6 +153,19 @@ impl ExecutionPayload { // Max size of variable length `withdrawals` field + (T::max_withdrawals_per_payload() * ::ssz_fixed_len()) } + + #[allow(clippy::integer_arithmetic)] + /// Returns the maximum size of an execution payload. + pub fn max_execution_payload_eip6110_size() -> usize { + // Fixed part + ExecutionPayloadEip6110::::default().as_ssz_bytes().len() + // Max size of variable length `extra_data` field + + (T::max_extra_data_bytes() * ::ssz_fixed_len()) + // Max size of variable length `transactions` field + + (T::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + T::max_bytes_per_transaction())) + // Max size of variable length `deposit_receipts` field + + (T::max_deposit_receipts_per_payload() * ::ssz_fixed_len()) + } } impl ForkVersionDeserialize for ExecutionPayload { @@ -162,6 +181,7 @@ impl ForkVersionDeserialize for ExecutionPayload { ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?), ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?), ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?), + ForkName::Eip6110 => Self::Eip6110(serde_json::from_value(value).map_err(convert_err)?), ForkName::Base | ForkName::Altair => { return Err(serde::de::Error::custom(format!( "ExecutionPayload failed to deserialize: unsupported fork '{}'", @@ -178,6 +198,7 @@ impl ExecutionPayload { ExecutionPayload::Merge(_) => ForkName::Merge, ExecutionPayload::Capella(_) => ForkName::Capella, ExecutionPayload::Deneb(_) => ForkName::Deneb, + ExecutionPayload::Eip6110(_) => ForkName::Eip6110, } } } diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index 57381b5327f..1bdd4dc22c3 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash; use BeaconStateError; #[superstruct( - variants(Merge, Capella, Deneb), + variants(Merge, Capella, Deneb, Eip6110), variant_attributes( derive( Default, @@ -74,13 +74,16 @@ pub struct ExecutionPayloadHeader { pub block_hash: ExecutionBlockHash, #[superstruct(getter(copy))] pub transactions_root: Hash256, - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Capella, Deneb, Eip6110))] #[superstruct(getter(copy))] pub withdrawals_root: Hash256, - #[superstruct(only(Deneb))] + #[superstruct(only(Deneb, Eip6110))] #[serde(with = "serde_utils::quoted_u256")] #[superstruct(getter(copy))] pub excess_data_gas: Uint256, + #[superstruct(only(Eip6110))] + #[superstruct(getter(copy))] + pub deposit_receipts: DepositReceipts, } impl ExecutionPayloadHeader { @@ -98,6 +101,9 @@ impl ExecutionPayloadHeader { ExecutionPayloadHeaderCapella::from_ssz_bytes(bytes).map(Self::Capella) } ForkName::Deneb => ExecutionPayloadHeaderDeneb::from_ssz_bytes(bytes).map(Self::Deneb), + ForkName::Eip6110 => { + ExecutionPayloadHeaderEip6110::from_ssz_bytes(bytes).map(Self::Eip6110) + } } } } @@ -157,6 +163,30 @@ impl ExecutionPayloadHeaderCapella { } } +impl ExecutionPayloadHeaderDeneb { + pub fn upgrade_to_eip6110(&self) -> ExecutionPayloadHeaderEip6110 { + ExecutionPayloadHeaderEip6110 { + 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, + excess_data_gas: Uint256::zero(), + deposit_receipts: DepositReceipts::::default(), + } + } +} + impl<'a, T: EthSpec> From<&'a ExecutionPayloadMerge> for ExecutionPayloadHeaderMerge { fn from(payload: &'a ExecutionPayloadMerge) -> Self { Self { @@ -222,6 +252,30 @@ impl<'a, T: EthSpec> From<&'a ExecutionPayloadDeneb> for ExecutionPayloadHead } } +impl<'a, T: EthSpec> From<&'a ExecutionPayloadEip6110> for ExecutionPayloadHeaderEip6110 { + fn from(payload: &'a ExecutionPayloadEip6110) -> 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(), + excess_data_gas: payload.excess_data_gas, + deposit_receipts: payload.deposit_receipts.clone(), + } + } +} + // 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, T: EthSpec> From<&'a Self> for ExecutionPayloadHeaderMerge { @@ -242,6 +296,12 @@ impl<'a, T: EthSpec> From<&'a Self> for ExecutionPayloadHeaderDeneb { } } +impl<'a, T: EthSpec> From<&'a Self> for ExecutionPayloadHeaderEip6110 { + fn from(payload: &'a Self) -> Self { + payload.clone() + } +} + impl<'a, T: EthSpec> From> for ExecutionPayloadHeader { fn from(payload: ExecutionPayloadRef<'a, T>) -> Self { map_execution_payload_ref_into_execution_payload_header!( @@ -282,6 +342,18 @@ impl TryFrom> for ExecutionPayloadHeaderDe } } +impl TryFrom> for ExecutionPayloadHeaderEip6110 { + type Error = BeaconStateError; + fn try_from(header: ExecutionPayloadHeader) -> Result { + match header { + ExecutionPayloadHeader::Eip6110(execution_payload_header) => { + Ok(execution_payload_header) + } + _ => Err(BeaconStateError::IncorrectStateVariant), + } + } +} + impl ForkVersionDeserialize for ExecutionPayloadHeader { fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>( value: serde_json::value::Value, @@ -298,6 +370,7 @@ impl ForkVersionDeserialize for ExecutionPayloadHeader { ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?), ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?), ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?), + ForkName::Eip6110 => Self::Eip6110(serde_json::from_value(value).map_err(convert_err)?), ForkName::Base | ForkName::Altair => { return Err(serde::de::Error::custom(format!( "ExecutionPayloadHeader failed to deserialize: unsupported fork '{}'", diff --git a/consensus/types/src/fork_context.rs b/consensus/types/src/fork_context.rs index 23163f0eecb..b369b8df504 100644 --- a/consensus/types/src/fork_context.rs +++ b/consensus/types/src/fork_context.rs @@ -61,6 +61,13 @@ impl ForkContext { )); } + if spec.eip6110_fork_epoch.is_some() { + fork_to_digest.push(( + ForkName::Eip6110, + ChainSpec::compute_fork_digest(spec.eip6110_fork_version, genesis_validators_root), + )); + } + let fork_to_digest: HashMap = fork_to_digest.into_iter().collect(); let digest_to_fork = fork_to_digest diff --git a/consensus/types/src/fork_name.rs b/consensus/types/src/fork_name.rs index e87b6d61ae1..607fe5a3abd 100644 --- a/consensus/types/src/fork_name.rs +++ b/consensus/types/src/fork_name.rs @@ -15,6 +15,7 @@ pub enum ForkName { Merge, Capella, Deneb, + Eip6110, } impl ForkName { @@ -25,6 +26,7 @@ impl ForkName { ForkName::Merge, ForkName::Capella, ForkName::Deneb, + ForkName::Eip6110, ] } @@ -43,6 +45,7 @@ impl ForkName { spec.bellatrix_fork_epoch = None; spec.capella_fork_epoch = None; spec.deneb_fork_epoch = None; + spec.eip6110_fork_epoch = None; spec } ForkName::Altair => { @@ -50,6 +53,7 @@ impl ForkName { spec.bellatrix_fork_epoch = None; spec.capella_fork_epoch = None; spec.deneb_fork_epoch = None; + spec.eip6110_fork_epoch = None; spec } ForkName::Merge => { @@ -57,6 +61,7 @@ impl ForkName { spec.bellatrix_fork_epoch = Some(Epoch::new(0)); spec.capella_fork_epoch = None; spec.deneb_fork_epoch = None; + spec.eip6110_fork_epoch = None; spec } ForkName::Capella => { @@ -64,6 +69,7 @@ impl ForkName { spec.bellatrix_fork_epoch = Some(Epoch::new(0)); spec.capella_fork_epoch = Some(Epoch::new(0)); spec.deneb_fork_epoch = None; + spec.eip6110_fork_epoch = None; spec } ForkName::Deneb => { @@ -71,6 +77,15 @@ impl ForkName { 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.eip6110_fork_epoch = None; + spec + } + ForkName::Eip6110 => { + 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.eip6110_fork_epoch = Some(Epoch::new(0)); spec } } @@ -86,6 +101,7 @@ impl ForkName { ForkName::Merge => Some(ForkName::Altair), ForkName::Capella => Some(ForkName::Merge), ForkName::Deneb => Some(ForkName::Capella), + ForkName::Eip6110 => Some(ForkName::Deneb), } } @@ -98,7 +114,8 @@ impl ForkName { ForkName::Altair => Some(ForkName::Merge), ForkName::Merge => Some(ForkName::Capella), ForkName::Capella => Some(ForkName::Deneb), - ForkName::Deneb => None, + ForkName::Deneb => Some(ForkName::Eip6110), + ForkName::Eip6110 => None, } } } @@ -148,6 +165,10 @@ macro_rules! map_fork_name_with { let (value, extra_data) = $body; ($t::Deneb(value), extra_data) } + ForkName::Eip6110 => { + let (value, extra_data) = $body; + ($t::Eip6110(value), extra_data) + } } }; } @@ -162,6 +183,7 @@ impl FromStr for ForkName { "bellatrix" | "merge" => ForkName::Merge, "capella" => ForkName::Capella, "deneb" => ForkName::Deneb, + "eip6110" => ForkName::Eip6110, _ => return Err(format!("unknown fork name: {}", fork_name)), }) } @@ -175,6 +197,7 @@ impl Display for ForkName { ForkName::Merge => "bellatrix".fmt(f), ForkName::Capella => "capella".fmt(f), ForkName::Deneb => "deneb".fmt(f), + ForkName::Eip6110 => "eip6110".fmt(f), } } } diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 46c5c2a4ce8..74762eed8a1 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -74,6 +74,7 @@ pub mod voluntary_exit; #[macro_use] pub mod slot_epoch_macros; pub mod config_and_preset; +pub mod eip6110; pub mod execution_block_header; pub mod fork_context; pub mod participation_flags; @@ -110,11 +111,13 @@ pub use crate::attestation_duty::AttestationDuty; pub use crate::attester_slashing::AttesterSlashing; pub use crate::beacon_block::{ BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockDeneb, - BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, EmptyBlock, + BeaconBlockEip6110, BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, + EmptyBlock, }; pub use crate::beacon_block_body::{ BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyCapella, - BeaconBlockBodyDeneb, BeaconBlockBodyMerge, BeaconBlockBodyRef, BeaconBlockBodyRefMut, + BeaconBlockBodyDeneb, BeaconBlockBodyEip6110, BeaconBlockBodyMerge, BeaconBlockBodyRef, + BeaconBlockBodyRefMut, }; pub use crate::beacon_block_header::BeaconBlockHeader; pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee}; @@ -131,18 +134,21 @@ pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH}; pub use crate::deposit_data::DepositData; pub use crate::deposit_message::DepositMessage; pub use crate::deposit_tree_snapshot::{DepositTreeSnapshot, FinalizedExecutionBlock}; +pub use crate::eip6110::DepositReceipt; pub use crate::enr_fork_id::EnrForkId; pub use crate::eth1_data::Eth1Data; pub use crate::eth_spec::EthSpecId; pub use crate::execution_block_hash::ExecutionBlockHash; pub use crate::execution_block_header::ExecutionBlockHeader; pub use crate::execution_payload::{ - ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge, - ExecutionPayloadRef, Transaction, Transactions, Withdrawals, + DepositReceipts, ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, + ExecutionPayloadEip6110, ExecutionPayloadMerge, ExecutionPayloadRef, Transaction, Transactions, + Withdrawals, }; pub use crate::execution_payload_header::{ ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, + ExecutionPayloadHeaderEip6110, ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, + ExecutionPayloadHeaderRefMut, }; pub use crate::fork::Fork; pub use crate::fork_context::ForkContext; @@ -158,8 +164,9 @@ pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_list::ParticipationList; pub use crate::payload::{ AbstractExecPayload, BlindedPayload, BlindedPayloadCapella, BlindedPayloadDeneb, - BlindedPayloadMerge, BlindedPayloadRef, BlockType, ExecPayload, FullPayload, - FullPayloadCapella, FullPayloadDeneb, FullPayloadMerge, FullPayloadRef, OwnedExecPayload, + BlindedPayloadEip6110, BlindedPayloadMerge, BlindedPayloadRef, BlockType, ExecPayload, + FullPayload, FullPayloadCapella, FullPayloadDeneb, FullPayloadEip6110, FullPayloadMerge, + FullPayloadRef, OwnedExecPayload, }; pub use crate::pending_attestation::PendingAttestation; pub use crate::preset::{AltairPreset, BasePreset, BellatrixPreset, CapellaPreset}; @@ -171,8 +178,9 @@ pub use crate::shuffling_id::AttestationShufflingId; pub use crate::signed_aggregate_and_proof::SignedAggregateAndProof; pub use crate::signed_beacon_block::{ ssz_tagged_signed_beacon_block, SignedBeaconBlock, SignedBeaconBlockAltair, - SignedBeaconBlockBase, SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockHash, - SignedBeaconBlockMerge, SignedBlindedBeaconBlock, + SignedBeaconBlockBase, SignedBeaconBlockCapella, SignedBeaconBlockDeneb, + SignedBeaconBlockEip6110, SignedBeaconBlockHash, SignedBeaconBlockMerge, + SignedBlindedBeaconBlock, }; pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader; pub use crate::signed_blob::*; diff --git a/consensus/types/src/payload.rs b/consensus/types/src/payload.rs index fd15bb5d5dc..1f2ba3600eb 100644 --- a/consensus/types/src/payload.rs +++ b/consensus/types/src/payload.rs @@ -39,6 +39,7 @@ pub trait ExecPayload: Debug + Clone + PartialEq + Hash + TreeHash + fn transactions(&self) -> Option<&Transactions>; /// fork-specific fields fn withdrawals_root(&self) -> Result; + fn deposit_receipts(&self) -> Result, Error>; /// Is this a default payload with 0x0 roots for transactions and withdrawals? fn is_default_with_zero_roots(&self) -> bool; @@ -82,12 +83,14 @@ pub trait AbstractExecPayload: + TryInto + TryInto + TryInto + + TryInto { type Ref<'a>: ExecPayload + Copy + From<&'a Self::Merge> + From<&'a Self::Capella> - + From<&'a Self::Deneb>; + + From<&'a Self::Deneb> + + From<&'a Self::Eip6110>; type Merge: OwnedExecPayload + Into @@ -101,12 +104,16 @@ pub trait AbstractExecPayload: + Into + for<'a> From>> + TryFrom>; + type Eip6110: OwnedExecPayload + + Into + + for<'a> From>> + + TryFrom>; fn default_at_fork(fork_name: ForkName) -> Result; } #[superstruct( - variants(Merge, Capella, Deneb), + variants(Merge, Capella, Deneb, Eip6110), variant_attributes( derive( Debug, @@ -147,6 +154,8 @@ pub struct FullPayload { pub execution_payload: ExecutionPayloadCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))] pub execution_payload: ExecutionPayloadDeneb, + #[superstruct(only(Eip6110), partial_getter(rename = "execution_payload_eip6110"))] + pub execution_payload: ExecutionPayloadEip6110, } impl From> for ExecutionPayload { @@ -253,6 +262,18 @@ impl ExecPayload for FullPayload { FullPayload::Deneb(ref inner) => { Ok(inner.execution_payload.withdrawals.tree_hash_root()) } + FullPayload::Eip6110(ref inner) => { + Ok(inner.execution_payload.withdrawals.tree_hash_root()) + } + } + } + + fn deposit_receipts(&self) -> Result, Error> { + match self { + FullPayload::Merge(_) => Err(Error::IncorrectStateVariant), + FullPayload::Capella(_) => Err(Error::IncorrectStateVariant), + FullPayload::Deneb(_) => Err(Error::IncorrectStateVariant), + FullPayload::Eip6110(ref inner) => Ok(inner.execution_payload.deposit_receipts.clone()), } } @@ -362,6 +383,18 @@ impl<'b, T: EthSpec> ExecPayload for FullPayloadRef<'b, T> { FullPayloadRef::Deneb(inner) => { Ok(inner.execution_payload.withdrawals.tree_hash_root()) } + FullPayloadRef::Eip6110(inner) => { + Ok(inner.execution_payload.withdrawals.tree_hash_root()) + } + } + } + + fn deposit_receipts(&self) -> Result, Error> { + match self { + FullPayloadRef::Merge(_) => Err(Error::IncorrectStateVariant), + FullPayloadRef::Capella(_) => Err(Error::IncorrectStateVariant), + FullPayloadRef::Deneb(_) => Err(Error::IncorrectStateVariant), + FullPayloadRef::Eip6110(inner) => Ok(inner.execution_payload.deposit_receipts.clone()), } } @@ -383,6 +416,7 @@ impl AbstractExecPayload for FullPayload { type Merge = FullPayloadMerge; type Capella = FullPayloadCapella; type Deneb = FullPayloadDeneb; + type Eip6110 = FullPayloadEip6110; fn default_at_fork(fork_name: ForkName) -> Result { match fork_name { @@ -390,6 +424,7 @@ impl AbstractExecPayload for FullPayload { ForkName::Merge => Ok(FullPayloadMerge::default().into()), ForkName::Capella => Ok(FullPayloadCapella::default().into()), ForkName::Deneb => Ok(FullPayloadDeneb::default().into()), + ForkName::Eip6110 => Ok(FullPayloadEip6110::default().into()), } } } @@ -410,7 +445,7 @@ impl TryFrom> for FullPayload { } #[superstruct( - variants(Merge, Capella, Deneb), + variants(Merge, Capella, Deneb, Eip6110), variant_attributes( derive( Debug, @@ -450,6 +485,8 @@ pub struct BlindedPayload { pub execution_payload_header: ExecutionPayloadHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))] pub execution_payload_header: ExecutionPayloadHeaderDeneb, + #[superstruct(only(Eip6110), partial_getter(rename = "execution_payload_eip6110"))] + pub execution_payload_header: ExecutionPayloadHeaderEip6110, } impl<'a, T: EthSpec> From> for BlindedPayload { @@ -532,6 +569,20 @@ impl ExecPayload for BlindedPayload { Ok(inner.execution_payload_header.withdrawals_root) } BlindedPayload::Deneb(ref inner) => Ok(inner.execution_payload_header.withdrawals_root), + BlindedPayload::Eip6110(ref inner) => { + Ok(inner.execution_payload_header.withdrawals_root) + } + } + } + + fn deposit_receipts(&self) -> Result, Error> { + match self { + BlindedPayload::Merge(_) => Err(Error::IncorrectStateVariant), + BlindedPayload::Capella(_) => Err(Error::IncorrectStateVariant), + BlindedPayload::Deneb(_) => Err(Error::IncorrectStateVariant), + BlindedPayload::Eip6110(ref inner) => { + Ok(inner.execution_payload_header.deposit_receipts.clone()) + } } } @@ -620,6 +671,20 @@ impl<'b, T: EthSpec> ExecPayload for BlindedPayloadRef<'b, T> { Ok(inner.execution_payload_header.withdrawals_root) } BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.withdrawals_root), + BlindedPayloadRef::Eip6110(inner) => { + Ok(inner.execution_payload_header.withdrawals_root) + } + } + } + + fn deposit_receipts(&self) -> Result, Error> { + match self { + BlindedPayloadRef::Merge(_) => Err(Error::IncorrectStateVariant), + BlindedPayloadRef::Capella(_) => Err(Error::IncorrectStateVariant), + BlindedPayloadRef::Deneb(_) => Err(Error::IncorrectStateVariant), + BlindedPayloadRef::Eip6110(inner) => { + Ok(inner.execution_payload_header.deposit_receipts.clone()) + } } } @@ -648,7 +713,8 @@ macro_rules! impl_exec_payload_common { $block_type_variant:ident, // Blinded | Full $is_default_with_empty_roots:block, $f:block, - $g:block) => { + $g:block, + $h:block) => { impl ExecPayload for $wrapper_type { fn block_type() -> BlockType { BlockType::$block_type_variant @@ -706,6 +772,11 @@ macro_rules! impl_exec_payload_common { let g = $g; g(self) } + + fn deposit_receipts(&self) -> Result, Error> { + let h = $h; + h(self) + } } impl From<$wrapped_type> for $wrapper_type { @@ -743,6 +814,15 @@ macro_rules! impl_exec_payload_for_fork { wrapper_ref_type.withdrawals_root() }; c + }, + { + let c: for<'a> fn( + &'a $wrapper_type_header, + ) -> Result, Error> = |payload: &$wrapper_type_header| { + let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload); + wrapper_ref_type.deposit_receipts() + }; + c } ); @@ -822,6 +902,14 @@ macro_rules! impl_exec_payload_for_fork { wrapper_ref_type.withdrawals_root() }; c + }, + { + let c: for<'a> fn(&'a $wrapper_type_full) -> Result, Error> = + |payload: &$wrapper_type_full| { + let wrapper_ref_type = FullPayloadRef::$fork_variant(&payload); + wrapper_ref_type.deposit_receipts() + }; + c } ); @@ -890,12 +978,20 @@ impl_exec_payload_for_fork!( ExecutionPayloadDeneb, Deneb ); +impl_exec_payload_for_fork!( + BlindedPayloadEip6110, + FullPayloadEip6110, + ExecutionPayloadHeaderEip6110, + ExecutionPayloadEip6110, + Eip6110 +); impl AbstractExecPayload for BlindedPayload { type Ref<'a> = BlindedPayloadRef<'a, T>; type Merge = BlindedPayloadMerge; type Capella = BlindedPayloadCapella; type Deneb = BlindedPayloadDeneb; + type Eip6110 = BlindedPayloadEip6110; fn default_at_fork(fork_name: ForkName) -> Result { match fork_name { @@ -903,6 +999,7 @@ impl AbstractExecPayload for BlindedPayload { ForkName::Merge => Ok(BlindedPayloadMerge::default().into()), ForkName::Capella => Ok(BlindedPayloadCapella::default().into()), ForkName::Deneb => Ok(BlindedPayloadDeneb::default().into()), + ForkName::Eip6110 => Ok(BlindedPayloadEip6110::default().into()), } } } @@ -936,6 +1033,11 @@ impl From> for BlindedPayload { execution_payload_header, }) } + ExecutionPayloadHeader::Eip6110(execution_payload_header) => { + Self::Eip6110(BlindedPayloadEip6110 { + execution_payload_header, + }) + } } } } @@ -952,6 +1054,9 @@ impl From> for ExecutionPayloadHeader { BlindedPayload::Deneb(blinded_payload) => { ExecutionPayloadHeader::Deneb(blinded_payload.execution_payload_header) } + BlindedPayload::Eip6110(blinded_payload) => { + ExecutionPayloadHeader::Eip6110(blinded_payload.execution_payload_header) + } } } } diff --git a/consensus/types/src/signed_beacon_block.rs b/consensus/types/src/signed_beacon_block.rs index 23a254079d2..3b45403a0c2 100644 --- a/consensus/types/src/signed_beacon_block.rs +++ b/consensus/types/src/signed_beacon_block.rs @@ -37,7 +37,7 @@ impl From for Hash256 { /// A `BeaconBlock` and a signature from its proposer. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb), + variants(Base, Altair, Merge, Capella, Deneb, Eip6110), variant_attributes( derive( Debug, @@ -78,6 +78,8 @@ pub struct SignedBeaconBlock = FullP pub message: BeaconBlockCapella, #[superstruct(only(Deneb), partial_getter(rename = "message_deneb"))] pub message: BeaconBlockDeneb, + #[superstruct(only(Eip6110), partial_getter(rename = "message_eip6110"))] + pub message: BeaconBlockEip6110, pub signature: Signature, } @@ -147,6 +149,9 @@ impl> SignedBeaconBlock BeaconBlock::Deneb(message) => { SignedBeaconBlock::Deneb(SignedBeaconBlockDeneb { message, signature }) } + BeaconBlock::Eip6110(message) => { + SignedBeaconBlock::Eip6110(SignedBeaconBlockEip6110 { message, signature }) + } } } @@ -440,6 +445,62 @@ impl SignedBeaconBlockDeneb> { } } +impl SignedBeaconBlockEip6110> { + pub fn into_full_block( + self, + execution_payload: ExecutionPayloadEip6110, + ) -> SignedBeaconBlockEip6110> { + let SignedBeaconBlockEip6110 { + message: + BeaconBlockEip6110 { + slot, + proposer_index, + parent_root, + state_root, + body: + BeaconBlockBodyEip6110 { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: BlindedPayloadEip6110 { .. }, + bls_to_execution_changes, + blob_kzg_commitments, + }, + }, + signature, + } = self; + SignedBeaconBlockEip6110 { + message: BeaconBlockEip6110 { + slot, + proposer_index, + parent_root, + state_root, + body: BeaconBlockBodyEip6110 { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadEip6110 { execution_payload }, + bls_to_execution_changes, + blob_kzg_commitments, + }, + }, + signature, + } + } +} + impl SignedBeaconBlock> { pub fn try_into_full_block( self, @@ -457,11 +518,15 @@ impl SignedBeaconBlock> { (SignedBeaconBlock::Deneb(block), Some(ExecutionPayload::Deneb(payload))) => { SignedBeaconBlock::Deneb(block.into_full_block(payload)) } + (SignedBeaconBlock::Eip6110(block), Some(ExecutionPayload::Eip6110(payload))) => { + SignedBeaconBlock::Eip6110(block.into_full_block(payload)) + } // avoid wildcard matching forks so that compiler will // direct us here when a new fork has been added (SignedBeaconBlock::Merge(_), _) => return None, (SignedBeaconBlock::Capella(_), _) => return None, (SignedBeaconBlock::Deneb(_), _) => return None, + (SignedBeaconBlock::Eip6110(_), _) => return None, }; Some(full_block) } @@ -604,6 +669,9 @@ pub mod ssz_tagged_signed_beacon_block { ForkName::Deneb => Ok(SignedBeaconBlock::Deneb( SignedBeaconBlockDeneb::from_ssz_bytes(body)?, )), + ForkName::Eip6110 => Ok(SignedBeaconBlock::Eip6110( + SignedBeaconBlockEip6110::from_ssz_bytes(body)?, + )), } } } diff --git a/lcli/src/create_payload_header.rs b/lcli/src/create_payload_header.rs index 5c96035851e..e5235eda727 100644 --- a/lcli/src/create_payload_header.rs +++ b/lcli/src/create_payload_header.rs @@ -6,7 +6,7 @@ use std::io::Write; use std::time::{SystemTime, UNIX_EPOCH}; use types::{ EthSpec, ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - ExecutionPayloadHeaderMerge, ForkName, + ExecutionPayloadHeaderEip6110, ExecutionPayloadHeaderMerge, ForkName, }; pub fn run(matches: &ArgMatches) -> Result<(), String> { @@ -48,6 +48,14 @@ pub fn run(matches: &ArgMatches) -> Result<(), String> { prev_randao: eth1_block_hash.into_root(), ..ExecutionPayloadHeaderDeneb::default() }), + ForkName::Eip6110 => ExecutionPayloadHeader::Eip6110(ExecutionPayloadHeaderEip6110 { + gas_limit, + base_fee_per_gas, + timestamp: genesis_time, + block_hash: eth1_block_hash, + prev_randao: eth1_block_hash.into_root(), + ..ExecutionPayloadHeaderEip6110::default() + }), }; let mut file = File::create(file_name).map_err(|_| "Unable to create file".to_string())?; diff --git a/lcli/src/main.rs b/lcli/src/main.rs index 18695d277ef..fa6ddcb2863 100644 --- a/lcli/src/main.rs +++ b/lcli/src/main.rs @@ -426,7 +426,7 @@ fn main() { .takes_value(true) .default_value("bellatrix") .help("The fork for which the execution payload header should be created.") - .possible_values(&["merge", "bellatrix", "capella", "deneb"]) + .possible_values(&["merge", "bellatrix", "capella", "deneb", "eip6110"]) ) ) .subcommand( @@ -611,6 +611,15 @@ fn main() { "The epoch at which to enable the deneb hard fork", ), ) + .arg( + Arg::with_name("eip6110-fork-epoch") + .long("eip6110-fork-epoch") + .value_name("EPOCH") + .takes_value(true) + .help( + "The epoch at which to enable the eip6110 hard fork", + ), + ) .arg( Arg::with_name("ttd") .long("ttd") diff --git a/lcli/src/new_testnet.rs b/lcli/src/new_testnet.rs index dfb9a283a0d..82843a232cb 100644 --- a/lcli/src/new_testnet.rs +++ b/lcli/src/new_testnet.rs @@ -14,14 +14,13 @@ use state_processing::upgrade::{upgrade_to_altair, upgrade_to_bellatrix}; use std::fs::File; use std::io::Read; use std::path::PathBuf; -use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; use types::ExecutionBlockHash; use types::{ test_utils::generate_deterministic_keypairs, Address, BeaconState, ChainSpec, Config, Epoch, Eth1Data, EthSpec, ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, - ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRefMut, - ForkName, Hash256, Keypair, PublicKey, Validator, + ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderEip6110, ExecutionPayloadHeaderMerge, + ExecutionPayloadHeaderRefMut, ForkName, Hash256, Keypair, PublicKey, Validator, }; pub fn run(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Result<(), String> { @@ -90,6 +89,10 @@ pub fn run(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Resul spec.deneb_fork_epoch = Some(fork_epoch); } + if let Some(fork_epoch) = parse_optional(matches, "eip6110-fork-epoch")? { + spec.eip6110_fork_epoch = Some(fork_epoch); + } + if let Some(ttd) = parse_optional(matches, "ttd")? { spec.terminal_total_difficulty = ttd; } @@ -120,6 +123,10 @@ pub fn run(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Resul ExecutionPayloadHeaderDeneb::::from_ssz_bytes(bytes.as_slice()) .map(ExecutionPayloadHeader::Deneb) } + ForkName::Eip6110 => { + ExecutionPayloadHeaderEip6110::::from_ssz_bytes(bytes.as_slice()) + .map(ExecutionPayloadHeader::Eip6110) + } } .map_err(|e| format!("SSZ decode failed: {:?}", e)) }) @@ -244,13 +251,11 @@ fn initialize_state_with_validators( }); let execution_payload_header = execution_payload_header.unwrap_or(default_header); // Empty eth1 data + // EIP-6110 disable Eth1Data voting by zeroing out the deposit root and block hash let eth1_data = Eth1Data { - block_hash: eth1_block_hash, + block_hash: Hash256::zero(), deposit_count: 0, - deposit_root: Hash256::from_str( - "0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e", - ) - .unwrap(), // empty deposit tree root + deposit_root: Hash256::zero(), }; let mut state = BeaconState::new(genesis_time, eth1_data, spec); @@ -324,6 +329,9 @@ fn initialize_state_with_validators( ExecutionPayloadHeaderRefMut::Deneb(_) => { return Err("Cannot start genesis from a deneb state".to_string()) } + ExecutionPayloadHeaderRefMut::Eip6110(_) => { + return Err("Cannot start genesis from an eip6110 state".to_string()) + } } } diff --git a/lcli/src/parse_ssz.rs b/lcli/src/parse_ssz.rs index d51ea3a2f15..191bafecd59 100644 --- a/lcli/src/parse_ssz.rs +++ b/lcli/src/parse_ssz.rs @@ -53,16 +53,19 @@ pub fn run_parse_ssz(matches: &ArgMatches) -> Result<(), String> { "signed_block_merge" => decode_and_print::>(&bytes, format)?, "signed_block_capella" => decode_and_print::>(&bytes, format)?, "signed_block_deneb" => decode_and_print::>(&bytes, format)?, + "signed_block_eip6110" => decode_and_print::>(&bytes, format)?, "block_base" => decode_and_print::>(&bytes, format)?, "block_altair" => decode_and_print::>(&bytes, format)?, "block_merge" => decode_and_print::>(&bytes, format)?, "block_capella" => decode_and_print::>(&bytes, format)?, "block_deneb" => decode_and_print::>(&bytes, format)?, + "block_eip6110" => decode_and_print::>(&bytes, format)?, "state_base" => decode_and_print::>(&bytes, format)?, "state_altair" => decode_and_print::>(&bytes, format)?, "state_merge" => decode_and_print::>(&bytes, format)?, "state_capella" => decode_and_print::>(&bytes, format)?, "state_deneb" => decode_and_print::>(&bytes, format)?, + "state_eip6110" => decode_and_print::>(&bytes, format)?, "blob_sidecar" => decode_and_print::>(&bytes, format)?, other => return Err(format!("Unknown type: {}", other)), }; diff --git a/scripts/local_testnet/setup.sh b/scripts/local_testnet/setup.sh index 60a5c98bd03..b76b6cbde85 100755 --- a/scripts/local_testnet/setup.sh +++ b/scripts/local_testnet/setup.sh @@ -29,6 +29,7 @@ lcli \ --bellatrix-fork-epoch $BELLATRIX_FORK_EPOCH \ --capella-fork-epoch $CAPELLA_FORK_EPOCH \ --deneb-fork-epoch $DENEB_FORK_EPOCH \ + --eip6110-fork-epoch $EIP6110_FORK_EPOCH \ --ttd $TTD \ --eth1-block-hash $ETH1_BLOCK_HASH \ --eth1-id $CHAIN_ID \ diff --git a/scripts/local_testnet/vars.env b/scripts/local_testnet/vars.env index 9daf7d236a7..10b2489e678 100644 --- a/scripts/local_testnet/vars.env +++ b/scripts/local_testnet/vars.env @@ -46,6 +46,7 @@ ALTAIR_FORK_EPOCH=0 BELLATRIX_FORK_EPOCH=0 CAPELLA_FORK_EPOCH=1 DENEB_FORK_EPOCH=2 +EIP6110_FORK_EPOCH=3 TTD=0 diff --git a/scripts/tests/vars.env b/scripts/tests/vars.env index 14707283c29..eb39a785c9f 100644 --- a/scripts/tests/vars.env +++ b/scripts/tests/vars.env @@ -43,6 +43,7 @@ ALTAIR_FORK_EPOCH=0 BELLATRIX_FORK_EPOCH=0 CAPELLA_FORK_EPOCH=1 DENEB_FORK_EPOCH=18446744073709551615 +EIP6110_FORK_EPOCH=18446744073709551615 TTD=0 diff --git a/testing/ef_tests/src/cases/common.rs b/testing/ef_tests/src/cases/common.rs index 68fd990f1fb..7ef8b6127f5 100644 --- a/testing/ef_tests/src/cases/common.rs +++ b/testing/ef_tests/src/cases/common.rs @@ -67,6 +67,7 @@ pub fn previous_fork(fork_name: ForkName) -> ForkName { ForkName::Merge => ForkName::Altair, ForkName::Capella => ForkName::Merge, ForkName::Deneb => ForkName::Capella, + ForkName::Eip6110 => ForkName::Deneb, } } diff --git a/testing/ef_tests/src/cases/epoch_processing.rs b/testing/ef_tests/src/cases/epoch_processing.rs index 34c4a74a9b6..3ad94fc3560 100644 --- a/testing/ef_tests/src/cases/epoch_processing.rs +++ b/testing/ef_tests/src/cases/epoch_processing.rs @@ -104,7 +104,8 @@ impl EpochTransition for JustificationAndFinalization { BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => { + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => { let justification_and_finalization_state = altair::process_justification_and_finalization( state, @@ -128,7 +129,8 @@ impl EpochTransition for RewardsAndPenalties { BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => altair::process_rewards_and_penalties( + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => altair::process_rewards_and_penalties( state, &altair::ParticipationCache::new(state, spec).unwrap(), spec, @@ -158,7 +160,8 @@ impl EpochTransition for Slashings { BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => { + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => { process_slashings( state, altair::ParticipationCache::new(state, spec) @@ -235,7 +238,8 @@ impl EpochTransition for SyncCommitteeUpdates { BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => altair::process_sync_committee_updates(state, spec), + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => altair::process_sync_committee_updates(state, spec), } } } @@ -247,7 +251,8 @@ impl EpochTransition for InactivityUpdates { BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => altair::process_inactivity_updates( + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => altair::process_inactivity_updates( state, &altair::ParticipationCache::new(state, spec).unwrap(), spec, @@ -263,7 +268,8 @@ impl EpochTransition for ParticipationFlagUpdates { BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => altair::process_participation_flag_updates(state), + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => altair::process_participation_flag_updates(state), } } } @@ -314,7 +320,7 @@ impl> Case for EpochProcessing { T::name() != "participation_record_updates" && T::name() != "historical_summaries_update" } - ForkName::Capella | ForkName::Deneb => { + ForkName::Capella | ForkName::Deneb | ForkName::Eip6110 => { T::name() != "participation_record_updates" && T::name() != "historical_roots_update" } diff --git a/testing/ef_tests/src/cases/fork.rs b/testing/ef_tests/src/cases/fork.rs index d27fbcd6715..e409f64c678 100644 --- a/testing/ef_tests/src/cases/fork.rs +++ b/testing/ef_tests/src/cases/fork.rs @@ -5,6 +5,7 @@ use crate::decode::{ssz_decode_state, yaml_decode_file}; use serde_derive::Deserialize; use state_processing::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, + upgrade_to_eip6110, }; use types::{BeaconState, ForkName}; @@ -65,6 +66,7 @@ impl Case for ForkTest { ForkName::Merge => upgrade_to_bellatrix(&mut result_state, spec).map(|_| result_state), ForkName::Capella => upgrade_to_capella(&mut result_state, spec).map(|_| result_state), ForkName::Deneb => upgrade_to_deneb(&mut result_state, spec).map(|_| result_state), + ForkName::Eip6110 => upgrade_to_eip6110(&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/operations.rs b/testing/ef_tests/src/cases/operations.rs index 2f27b43a1df..77092c69c0e 100644 --- a/testing/ef_tests/src/cases/operations.rs +++ b/testing/ef_tests/src/cases/operations.rs @@ -7,7 +7,7 @@ use serde_derive::Deserialize; use state_processing::{ per_block_processing::{ errors::BlockProcessingError, - process_block_header, process_execution_payload, + process_block_header, process_deposit_receipt, process_execution_payload, process_operations::{ altair, base, process_attester_slashings, process_bls_to_execution_changes, process_deposits, process_exits, process_proposer_slashings, @@ -20,8 +20,8 @@ use std::fmt::Debug; use std::path::Path; use types::{ Attestation, AttesterSlashing, BeaconBlock, BeaconState, BlindedPayload, ChainSpec, Deposit, - EthSpec, ExecutionPayload, ForkName, FullPayload, ProposerSlashing, SignedBlsToExecutionChange, - SignedVoluntaryExit, SyncAggregate, + DepositReceipt, EthSpec, ExecutionPayload, ForkName, FullPayload, ProposerSlashing, + SignedBlsToExecutionChange, SignedVoluntaryExit, SyncAggregate, }; #[derive(Debug, Clone, Default, Deserialize)] @@ -98,7 +98,8 @@ impl Operation for Attestation { BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => { + | BeaconState::Deneb(_) + | BeaconState::Eip6110(_) => { altair::process_attestation(state, self, 0, &mut ctxt, VerifySignatures::True, spec) } } @@ -366,6 +367,38 @@ impl Operation for WithdrawalsPayload { } } +impl Operation for DepositReceipt { + fn handler_name() -> String { + "deposit_receipt".into() + } + + fn filename() -> String { + "deposit_receipt.ssz_snappy".into() + } + + fn is_enabled_for_fork(fork_name: ForkName) -> bool { + fork_name != ForkName::Base + && fork_name != ForkName::Altair + && fork_name != ForkName::Merge + && fork_name != ForkName::Capella + && fork_name != ForkName::Deneb + && fork_name != ForkName::Eip6110 + } + + fn decode(path: &Path, _fork_name: ForkName, _spec: &ChainSpec) -> Result { + ssz_decode_file(path) + } + + fn apply_to( + &self, + state: &mut BeaconState, + spec: &ChainSpec, + _: &Operations, + ) -> Result<(), BlockProcessingError> { + process_deposit_receipt(state, self, spec) + } +} + impl Operation for SignedBlsToExecutionChange { fn handler_name() -> String { "bls_to_execution_change".into() diff --git a/testing/ef_tests/src/cases/transition.rs b/testing/ef_tests/src/cases/transition.rs index 5c6da900e61..531517a7ed0 100644 --- a/testing/ef_tests/src/cases/transition.rs +++ b/testing/ef_tests/src/cases/transition.rs @@ -53,6 +53,13 @@ impl LoadCase for TransitionTest { spec.capella_fork_epoch = Some(Epoch::new(0)); spec.deneb_fork_epoch = Some(metadata.fork_epoch); } + ForkName::Eip6110 => { + 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.eip6110_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 6dec9346291..556c9a2da90 100644 --- a/testing/ef_tests/src/handler.rs +++ b/testing/ef_tests/src/handler.rs @@ -222,6 +222,10 @@ impl SszStaticHandler { Self::for_forks(vec![ForkName::Deneb]) } + pub fn eip6110_only() -> Self { + Self::for_forks(vec![ForkName::Eip6110]) + } + pub fn altair_and_later() -> Self { Self::for_forks(ForkName::list_all()[1..].to_vec()) } diff --git a/testing/ef_tests/src/type_name.rs b/testing/ef_tests/src/type_name.rs index ef128440301..a324ad073aa 100644 --- a/testing/ef_tests/src/type_name.rs +++ b/testing/ef_tests/src/type_name.rs @@ -49,6 +49,7 @@ type_name_generic!(BeaconBlockBodyAltair, "BeaconBlockBody"); type_name_generic!(BeaconBlockBodyMerge, "BeaconBlockBody"); type_name_generic!(BeaconBlockBodyCapella, "BeaconBlockBody"); type_name_generic!(BeaconBlockBodyDeneb, "BeaconBlockBody"); +type_name_generic!(BeaconBlockBodyEip6110, "BeaconBlockBody"); type_name!(BeaconBlockHeader); type_name_generic!(BeaconState); type_name!(BlobIdentifier); @@ -56,6 +57,7 @@ type_name_generic!(BlobSidecar); type_name!(Checkpoint); type_name_generic!(ContributionAndProof); type_name!(Deposit); +type_name!(DepositReceipt); type_name!(DepositData); type_name!(DepositMessage); type_name!(Eth1Data); @@ -63,11 +65,13 @@ type_name_generic!(ExecutionPayload); type_name_generic!(ExecutionPayloadMerge, "ExecutionPayload"); type_name_generic!(ExecutionPayloadCapella, "ExecutionPayload"); type_name_generic!(ExecutionPayloadDeneb, "ExecutionPayload"); +type_name_generic!(ExecutionPayloadEip6110, "ExecutionPayload"); type_name_generic!(FullPayload, "ExecutionPayload"); type_name_generic!(ExecutionPayloadHeader); type_name_generic!(ExecutionPayloadHeaderMerge, "ExecutionPayloadHeader"); type_name_generic!(ExecutionPayloadHeaderCapella, "ExecutionPayloadHeader"); type_name_generic!(ExecutionPayloadHeaderDeneb, "ExecutionPayloadHeader"); +type_name_generic!(ExecutionPayloadHeaderEip6110, "ExecutionPayloadHeader"); type_name_generic!(BlindedPayload, "ExecutionPayloadHeader"); type_name!(Fork); type_name!(ForkData); diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index 7d9d8d90853..c18ab2c5982 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -34,6 +34,12 @@ fn operations_deposit() { OperationsHandler::::default().run(); } +#[test] +fn operations_deposit_receipt() { + OperationsHandler::::default().run(); + OperationsHandler::::default().run(); +} + #[test] fn operations_exit() { OperationsHandler::::default().run(); @@ -272,6 +278,10 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::deneb_only() .run(); + SszStaticHandler::, MinimalEthSpec>::eip6110_only() + .run(); + SszStaticHandler::, MainnetEthSpec>::eip6110_only() + .run(); } // Altair and later @@ -336,6 +346,10 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::deneb_only() .run(); + SszStaticHandler::, MinimalEthSpec>::eip6110_only() + .run(); + SszStaticHandler::, MainnetEthSpec>::eip6110_only() + .run(); } #[test] @@ -352,6 +366,10 @@ mod ssz_static { ::deneb_only().run(); SszStaticHandler::, MainnetEthSpec> ::deneb_only().run(); + SszStaticHandler::, MinimalEthSpec> + ::eip6110_only().run(); + SszStaticHandler::, MainnetEthSpec> + ::eip6110_only().run(); } #[test] diff --git a/validator_client/src/signing_method/web3signer.rs b/validator_client/src/signing_method/web3signer.rs index d7d74c94487..739b20d116e 100644 --- a/validator_client/src/signing_method/web3signer.rs +++ b/validator_client/src/signing_method/web3signer.rs @@ -28,6 +28,7 @@ pub enum ForkName { Bellatrix, Capella, Deneb, + Eip6110, } #[derive(Debug, PartialEq, Serialize)] @@ -101,6 +102,11 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload> Web3SignerObject<'a, T, Pa block: None, block_header: Some(block.block_header()), }), + BeaconBlock::Eip6110(_) => Ok(Web3SignerObject::BeaconBlock { + version: ForkName::Eip6110, + block: None, + block_header: Some(block.block_header()), + }), } }