Compare commits

...

4 Commits

Author SHA1 Message Date
Stefan
65bb2836be fix: set slot_number to latest_block_header.slot in Gloas fork upgrade (#8726)
During the Fulu→Gloas state upgrade, the latestExecutionPayloadHeader's
slot_number was hardcoded to 0. This caused beacon state root divergence
between Lighthouse and Lodestar at the Gloas fork boundary (slot 8).

Set slot_number to pre.latest_block_header.slot to match Lodestar's
behavior and correctly carry forward the slot of the last block.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 15:42:22 +05:30
Pawan Dhananjay
a17c447680 Add getPayload v2 methods 2026-01-21 17:21:06 -08:00
Pawan Dhananjay
658163cfde Testing 2026-01-19 15:21:04 -08:00
Pawan Dhananjay
47094d592c Add bal support, remove epbs stuff 2026-01-16 12:40:57 -08:00
27 changed files with 1240 additions and 278 deletions

View File

@@ -1,5 +1,5 @@
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes, BlockProcessStatus, metrics};
use execution_layer::{ExecutionLayer, ExecutionPayloadBodyV1};
use execution_layer::{ExecutionLayer, ExecutionPayloadBody};
use logging::crit;
use std::collections::HashMap;
use std::sync::Arc;
@@ -58,7 +58,7 @@ struct BodiesByRange<E: EthSpec> {
struct BlockParts<E: EthSpec> {
blinded_block: Box<SignedBlindedBeaconBlock<E>>,
header: Box<ExecutionPayloadHeader<E>>,
body: Option<Box<ExecutionPayloadBodyV1<E>>>,
body: Option<Box<ExecutionPayloadBody<E>>>,
}
impl<E: EthSpec> BlockParts<E> {
@@ -634,7 +634,9 @@ impl<T: BeaconChainTypes> BeaconBlockStreamer<T> {
.map_err(BeaconChainError::EngineGetCapabilititesFailed)
{
Ok(engine_capabilities) => {
if engine_capabilities.get_payload_bodies_by_range_v1 {
if engine_capabilities.get_payload_bodies_by_range_v2
|| engine_capabilities.get_payload_bodies_by_range_v1
{
self.stream_blocks(block_roots, sender).await;
} else {
// use the fallback method

View File

@@ -5765,7 +5765,60 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
execution_payload_value,
)
}
BeaconState::Gloas(_) => return Err(BlockProductionError::GloasNotImplemented),
BeaconState::Gloas(_) => {
let (
payload,
kzg_commitments,
maybe_blobs_and_proofs,
maybe_requests,
execution_payload_value,
) = block_contents
.ok_or(BlockProductionError::MissingExecutionPayload)?
.deconstruct();
(
BeaconBlock::Gloas(BeaconBlockGloas {
slot,
proposer_index,
parent_root,
state_root: Hash256::zero(),
body: BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings: proposer_slashings
.try_into()
.map_err(BlockProductionError::SszTypesError)?,
attester_slashings: attester_slashings_electra
.try_into()
.map_err(BlockProductionError::SszTypesError)?,
attestations: attestations_electra
.try_into()
.map_err(BlockProductionError::SszTypesError)?,
deposits: deposits
.try_into()
.map_err(BlockProductionError::SszTypesError)?,
voluntary_exits: voluntary_exits
.try_into()
.map_err(BlockProductionError::SszTypesError)?,
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
.try_into()
.map_err(BlockProductionError::SszTypesError)?,
blob_kzg_commitments: kzg_commitments
.ok_or(BlockProductionError::InvalidPayloadFork)?,
execution_requests: maybe_requests
.ok_or(BlockProductionError::MissingExecutionRequests)?,
},
}),
maybe_blobs_and_proofs,
execution_payload_value,
)
}
};
let block = SignedBeaconBlock::from_block(
@@ -6069,6 +6122,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
None
};
let slot_number = if prepare_slot_fork.gloas_enabled() {
Some(prepare_slot.as_u64())
} else {
None
};
let payload_attributes = PayloadAttributes::new(
self.slot_clock
.start_of(prepare_slot)
@@ -6078,6 +6137,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
execution_layer.get_suggested_fee_recipient(proposer).await,
withdrawals.map(Into::into),
parent_beacon_block_root,
slot_number,
);
execution_layer

View File

@@ -511,12 +511,20 @@ where
let suggested_fee_recipient = execution_layer
.get_suggested_fee_recipient(proposer_index)
.await;
let slot_number = if fork.gloas_enabled() {
Some(builder_params.slot.as_u64())
} else {
None
};
let payload_attributes = PayloadAttributes::new(
timestamp,
random,
suggested_fee_recipient,
withdrawals,
parent_beacon_block_root,
slot_number,
);
let target_gas_limit = execution_layer.get_proposer_gas_limit(proposer_index).await;

View File

@@ -1021,6 +1021,7 @@ async fn payload_preparation() {
fee_recipient,
None,
None,
None,
);
assert_eq!(rig.previous_payload_attributes(), payload_attributes);
}

View File

@@ -41,6 +41,14 @@ pub fn calculate_execution_block_hash<E: EthSpec>(
let rlp_excess_blob_gas = payload.excess_blob_gas().ok();
let requests_root = execution_requests.map(|requests| requests.requests_hash());
// Calculate block access list root (Gloas/Amsterdam).
// The BAL root is the keccak256 hash of the BAL bytes.
let rlp_block_access_list_root = payload
.block_access_list()
.ok()
.map(|bal| keccak256(&bal[..]));
let rlp_slot_number = payload.slot_number().ok();
// Construct the block header.
let exec_block_header = ExecutionBlockHeader::from_payload(
payload,
@@ -51,6 +59,8 @@ pub fn calculate_execution_block_hash<E: EthSpec>(
rlp_excess_blob_gas,
parent_beacon_block_root,
requests_root,
rlp_block_access_list_root,
rlp_slot_number,
);
// Hash the RLP encoding of the block header.
@@ -122,8 +132,10 @@ mod test {
excess_blob_gas: None,
parent_beacon_block_root: None,
requests_root: None,
block_access_list_root: None,
slot_number: None,
};
let expected_rlp = "f90200a0e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ba5e000000000000000000000000000000000000a0ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7a050f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accfa029b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000188016345785d8a00008301553482079e42a0000000000000000000000000000000000000000000000000000000000000000088000000000000000082036b";
let expected_rlp = "f90200a0e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347394ba5e000000000000000000000000000000000000a0ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7a050f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accfa029b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000188016345785d8a00008301553482079e42a0000000000000000000000000000000000000000000000000000000000000000088000000000000000082036b";
let expected_hash =
Hash256::from_str("6a251c7c3c5dca7b42407a3752ff48f3bbca1fab7f9868371d9918daf1988d1f")
.unwrap();
@@ -154,6 +166,8 @@ mod test {
excess_blob_gas: None,
parent_beacon_block_root: None,
requests_root: None,
block_access_list_root: None,
slot_number: None,
};
let expected_rlp = "f901fda0927ca537f06c783a3a2635b8805eef1c8c2124f7444ad4a3389898dd832f2dbea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ba5e000000000000000000000000000000000000a0e97859b065bd8dbbb4519c7cb935024de2484c2b7f881181b4360492f0b06b82a050f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accfa029b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800188016345785d8a00008301553482079e42a0000000000000000000000000000000000000000000000000000000000002000088000000000000000082036b";
let expected_hash =
@@ -187,6 +201,8 @@ mod test {
excess_blob_gas: None,
parent_beacon_block_root: None,
requests_root: None,
block_access_list_root: None,
slot_number: None,
};
let expected_hash =
Hash256::from_str("6da69709cd5a34079b6604d29cd78fc01dacd7c6268980057ad92a2bede87351")
@@ -218,6 +234,8 @@ mod test {
excess_blob_gas: Some(0x0u64),
parent_beacon_block_root: Some(Hash256::from_str("f7d327d2c04e4f12e9cdd492e53d39a1d390f8b1571e3b2a22ac6e1e170e5b1a").unwrap()),
requests_root: None,
block_access_list_root: None,
slot_number: None,
};
let expected_hash =
Hash256::from_str("a7448e600ead0a23d16f96aa46e8dea9eef8a7c5669a5f0a5ff32709afe9c408")
@@ -249,6 +267,8 @@ mod test {
excess_blob_gas: Some(44695552),
parent_beacon_block_root: Some(Hash256::from_str("f3a888fee010ebb1ae083547004e96c254b240437823326fdff8354b1fc25629").unwrap()),
requests_root: Some(Hash256::from_str("9440d3365f07573919e1e9ac5178c20ec6fe267357ee4baf8b6409901f331b62").unwrap()),
block_access_list_root: None,
slot_number: None,
};
let expected_hash =
Hash256::from_str("61e67afc96bf21be6aab52c1ace1db48de7b83f03119b0644deb4b69e87e09e1")

View File

@@ -1,11 +1,13 @@
use crate::engines::ForkchoiceState;
use crate::http::{
ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3,
ENGINE_GET_BLOBS_V1, ENGINE_GET_BLOBS_V2, ENGINE_GET_CLIENT_VERSION_V1,
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_GET_PAYLOAD_V4,
ENGINE_GET_PAYLOAD_V5, ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3,
ENGINE_NEW_PAYLOAD_V4,
ENGINE_FORKCHOICE_UPDATED_V4, ENGINE_GET_BLOBS_V1, ENGINE_GET_BLOBS_V2,
ENGINE_GET_CLIENT_VERSION_V1, ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2, ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2, ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2,
ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4, ENGINE_GET_PAYLOAD_V5, ENGINE_GET_PAYLOAD_V6,
ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V4,
ENGINE_NEW_PAYLOAD_V5,
};
use eth2::types::{
BlobsBundle, SsePayloadAttributes, SsePayloadAttributesV1, SsePayloadAttributesV2,
@@ -16,6 +18,7 @@ pub use json_structures::{JsonWithdrawal, TransitionConfigurationV1};
use pretty_reqwest_error::PrettyReqwestError;
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
use ssz_types::VariableList;
use strum::IntoStaticStr;
use superstruct::superstruct;
pub use types::{
@@ -158,7 +161,7 @@ impl ExecutionBlock {
}
#[superstruct(
variants(V1, V2, V3),
variants(V1, V2, V3, V4),
variant_attributes(derive(Clone, Debug, Eq, Hash, PartialEq),),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
@@ -171,10 +174,12 @@ pub struct PayloadAttributes {
pub prev_randao: Hash256,
#[superstruct(getter(copy))]
pub suggested_fee_recipient: Address,
#[superstruct(only(V2, V3))]
#[superstruct(only(V2, V3, V4))]
pub withdrawals: Vec<Withdrawal>,
#[superstruct(only(V3), partial_getter(copy))]
#[superstruct(only(V3, V4), partial_getter(copy))]
pub parent_beacon_block_root: Hash256,
#[superstruct(only(V4), partial_getter(copy))]
pub slot_number: u64,
}
impl PayloadAttributes {
@@ -184,24 +189,35 @@ impl PayloadAttributes {
suggested_fee_recipient: Address,
withdrawals: Option<Vec<Withdrawal>>,
parent_beacon_block_root: Option<Hash256>,
slot_number: Option<u64>,
) -> Self {
match withdrawals {
Some(withdrawals) => match parent_beacon_block_root {
Some(parent_beacon_block_root) => PayloadAttributes::V3(PayloadAttributesV3 {
match (withdrawals, parent_beacon_block_root, slot_number) {
(Some(withdrawals), Some(parent_beacon_block_root), Some(slot_number)) => {
PayloadAttributes::V4(PayloadAttributesV4 {
timestamp,
prev_randao,
suggested_fee_recipient,
withdrawals,
parent_beacon_block_root,
}),
None => PayloadAttributes::V2(PayloadAttributesV2 {
slot_number,
})
}
(Some(withdrawals), Some(parent_beacon_block_root), None) => {
PayloadAttributes::V3(PayloadAttributesV3 {
timestamp,
prev_randao,
suggested_fee_recipient,
withdrawals,
}),
},
None => PayloadAttributes::V1(PayloadAttributesV1 {
parent_beacon_block_root,
})
}
(Some(withdrawals), None, _) => PayloadAttributes::V2(PayloadAttributesV2 {
timestamp,
prev_randao,
suggested_fee_recipient,
withdrawals,
}),
(None, _, _) => PayloadAttributes::V1(PayloadAttributesV1 {
timestamp,
prev_randao,
suggested_fee_recipient,
@@ -246,6 +262,21 @@ impl From<PayloadAttributes> for SsePayloadAttributes {
withdrawals,
parent_beacon_block_root,
}),
// V4 maps to V3 for SSE since we're ignoring SSE for devnet
PayloadAttributes::V4(PayloadAttributesV4 {
timestamp,
prev_randao,
suggested_fee_recipient,
withdrawals,
parent_beacon_block_root,
slot_number: _,
}) => Self::V3(SsePayloadAttributesV3 {
timestamp,
prev_randao,
suggested_fee_recipient,
withdrawals,
parent_beacon_block_root,
}),
}
}
}
@@ -395,10 +426,18 @@ impl<E: EthSpec> GetPayloadResponse<E> {
}
}
#[superstruct(
variants(V1, V2),
variant_attributes(derive(Clone, Debug)),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
)]
#[derive(Clone, Debug)]
pub struct ExecutionPayloadBodyV1<E: EthSpec> {
pub struct ExecutionPayloadBody<E: EthSpec> {
pub transactions: Transactions<E>,
pub withdrawals: Option<Withdrawals<E>>,
#[superstruct(only(V2))]
pub block_access_list: Option<VariableList<u8, E::MaxBytesPerTransaction>>,
}
impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
@@ -541,6 +580,228 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
))
}
}
ExecutionPayloadHeader::Gloas(header) => {
if let Some(withdrawals) = self.withdrawals {
Ok(ExecutionPayload::Gloas(ExecutionPayloadGloas {
parent_hash: header.parent_hash,
fee_recipient: header.fee_recipient,
state_root: header.state_root,
receipts_root: header.receipts_root,
logs_bloom: header.logs_bloom,
prev_randao: header.prev_randao,
block_number: header.block_number,
gas_limit: header.gas_limit,
gas_used: header.gas_used,
timestamp: header.timestamp,
extra_data: header.extra_data,
base_fee_per_gas: header.base_fee_per_gas,
block_hash: header.block_hash,
transactions: self.transactions,
withdrawals,
blob_gas_used: header.blob_gas_used,
excess_blob_gas: header.excess_blob_gas,
// block_access_list is not available via getPayloadBodiesByHash/Range
// so we initialize it as empty here. The full payload with block_access_list
// should be obtained via getPayload when needed.
block_access_list: VariableList::empty(),
slot_number: header.slot_number,
}))
} else {
Err(format!(
"block {} is post capella but payload body doesn't have withdrawals",
header.block_hash
))
}
}
}
}
}
impl<E: EthSpec> ExecutionPayloadBodyV2<E> {
pub fn to_payload(
self,
header: ExecutionPayloadHeader<E>,
) -> Result<ExecutionPayload<E>, String> {
match header {
ExecutionPayloadHeader::Bellatrix(header) => {
if self.withdrawals.is_some() {
return Err(format!(
"block {} is bellatrix but payload body has withdrawals",
header.block_hash
));
}
Ok(ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
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,
}))
}
ExecutionPayloadHeader::Capella(header) => {
if let Some(withdrawals) = self.withdrawals {
Ok(ExecutionPayload::Capella(ExecutionPayloadCapella {
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,
}))
} else {
Err(format!(
"block {} is capella but payload body doesn't have withdrawals",
header.block_hash
))
}
}
ExecutionPayloadHeader::Deneb(header) => {
if let Some(withdrawals) = self.withdrawals {
Ok(ExecutionPayload::Deneb(ExecutionPayloadDeneb {
parent_hash: header.parent_hash,
fee_recipient: header.fee_recipient,
state_root: header.state_root,
receipts_root: header.receipts_root,
logs_bloom: header.logs_bloom,
prev_randao: header.prev_randao,
block_number: header.block_number,
gas_limit: header.gas_limit,
gas_used: header.gas_used,
timestamp: header.timestamp,
extra_data: header.extra_data,
base_fee_per_gas: header.base_fee_per_gas,
block_hash: header.block_hash,
transactions: self.transactions,
withdrawals,
blob_gas_used: header.blob_gas_used,
excess_blob_gas: header.excess_blob_gas,
}))
} else {
Err(format!(
"block {} is post capella but payload body doesn't have withdrawals",
header.block_hash
))
}
}
ExecutionPayloadHeader::Electra(header) => {
if let Some(withdrawals) = self.withdrawals {
Ok(ExecutionPayload::Electra(ExecutionPayloadElectra {
parent_hash: header.parent_hash,
fee_recipient: header.fee_recipient,
state_root: header.state_root,
receipts_root: header.receipts_root,
logs_bloom: header.logs_bloom,
prev_randao: header.prev_randao,
block_number: header.block_number,
gas_limit: header.gas_limit,
gas_used: header.gas_used,
timestamp: header.timestamp,
extra_data: header.extra_data,
base_fee_per_gas: header.base_fee_per_gas,
block_hash: header.block_hash,
transactions: self.transactions,
withdrawals,
blob_gas_used: header.blob_gas_used,
excess_blob_gas: header.excess_blob_gas,
}))
} else {
Err(format!(
"block {} is post capella but payload body doesn't have withdrawals",
header.block_hash
))
}
}
ExecutionPayloadHeader::Fulu(header) => {
if let Some(withdrawals) = self.withdrawals {
Ok(ExecutionPayload::Fulu(ExecutionPayloadFulu {
parent_hash: header.parent_hash,
fee_recipient: header.fee_recipient,
state_root: header.state_root,
receipts_root: header.receipts_root,
logs_bloom: header.logs_bloom,
prev_randao: header.prev_randao,
block_number: header.block_number,
gas_limit: header.gas_limit,
gas_used: header.gas_used,
timestamp: header.timestamp,
extra_data: header.extra_data,
base_fee_per_gas: header.base_fee_per_gas,
block_hash: header.block_hash,
transactions: self.transactions,
withdrawals,
blob_gas_used: header.blob_gas_used,
excess_blob_gas: header.excess_blob_gas,
}))
} else {
Err(format!(
"block {} is post capella but payload body doesn't have withdrawals",
header.block_hash
))
}
}
ExecutionPayloadHeader::Gloas(header) => {
if let Some(withdrawals) = self.withdrawals {
Ok(ExecutionPayload::Gloas(ExecutionPayloadGloas {
parent_hash: header.parent_hash,
fee_recipient: header.fee_recipient,
state_root: header.state_root,
receipts_root: header.receipts_root,
logs_bloom: header.logs_bloom,
prev_randao: header.prev_randao,
block_number: header.block_number,
gas_limit: header.gas_limit,
gas_used: header.gas_used,
timestamp: header.timestamp,
extra_data: header.extra_data,
base_fee_per_gas: header.base_fee_per_gas,
block_hash: header.block_hash,
transactions: self.transactions,
withdrawals,
blob_gas_used: header.blob_gas_used,
excess_blob_gas: header.excess_blob_gas,
// V2 provides block_access_list from EL; use empty if not present
block_access_list: self
.block_access_list
.unwrap_or_else(VariableList::empty),
slot_number: header.slot_number,
}))
} else {
Err(format!(
"block {} is post capella but payload body doesn't have withdrawals",
header.block_hash
))
}
}
}
}
}
impl<E: EthSpec> ExecutionPayloadBody<E> {
pub fn to_payload(
self,
header: ExecutionPayloadHeader<E>,
) -> Result<ExecutionPayload<E>, String> {
match self {
ExecutionPayloadBody::V1(body) => body.to_payload(header),
ExecutionPayloadBody::V2(body) => body.to_payload(header),
}
}
}
@@ -551,16 +812,21 @@ pub struct EngineCapabilities {
pub new_payload_v2: bool,
pub new_payload_v3: bool,
pub new_payload_v4: bool,
pub new_payload_v5: bool,
pub forkchoice_updated_v1: bool,
pub forkchoice_updated_v2: bool,
pub forkchoice_updated_v3: bool,
pub forkchoice_updated_v4: bool,
pub get_payload_bodies_by_hash_v1: bool,
pub get_payload_bodies_by_hash_v2: bool,
pub get_payload_bodies_by_range_v1: bool,
pub get_payload_bodies_by_range_v2: bool,
pub get_payload_v1: bool,
pub get_payload_v2: bool,
pub get_payload_v3: bool,
pub get_payload_v4: bool,
pub get_payload_v5: bool,
pub get_payload_v6: bool,
pub get_client_version_v1: bool,
pub get_blobs_v1: bool,
pub get_blobs_v2: bool,
@@ -581,6 +847,9 @@ impl EngineCapabilities {
if self.new_payload_v4 {
response.push(ENGINE_NEW_PAYLOAD_V4);
}
if self.new_payload_v5 {
response.push(ENGINE_NEW_PAYLOAD_V5);
}
if self.forkchoice_updated_v1 {
response.push(ENGINE_FORKCHOICE_UPDATED_V1);
}
@@ -590,12 +859,21 @@ impl EngineCapabilities {
if self.forkchoice_updated_v3 {
response.push(ENGINE_FORKCHOICE_UPDATED_V3);
}
if self.forkchoice_updated_v4 {
response.push(ENGINE_FORKCHOICE_UPDATED_V4);
}
if self.get_payload_bodies_by_hash_v1 {
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1);
}
if self.get_payload_bodies_by_hash_v2 {
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2);
}
if self.get_payload_bodies_by_range_v1 {
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1);
}
if self.get_payload_bodies_by_range_v2 {
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2);
}
if self.get_payload_v1 {
response.push(ENGINE_GET_PAYLOAD_V1);
}
@@ -611,6 +889,9 @@ impl EngineCapabilities {
if self.get_payload_v5 {
response.push(ENGINE_GET_PAYLOAD_V5);
}
if self.get_payload_v6 {
response.push(ENGINE_GET_PAYLOAD_V6);
}
if self.get_client_version_v1 {
response.push(ENGINE_GET_CLIENT_VERSION_V1);
}

View File

@@ -11,6 +11,7 @@ use serde_json::json;
use std::collections::HashSet;
use std::sync::LazyLock;
use tokio::sync::Mutex;
use tracing::{debug, error};
use std::time::{Duration, Instant};
@@ -35,6 +36,7 @@ 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_V4: &str = "engine_newPayloadV4";
pub const ENGINE_NEW_PAYLOAD_V5: &str = "engine_newPayloadV5";
pub const ENGINE_NEW_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(8);
pub const ENGINE_GET_PAYLOAD_V1: &str = "engine_getPayloadV1";
@@ -42,15 +44,19 @@ pub const ENGINE_GET_PAYLOAD_V2: &str = "engine_getPayloadV2";
pub const ENGINE_GET_PAYLOAD_V3: &str = "engine_getPayloadV3";
pub const ENGINE_GET_PAYLOAD_V4: &str = "engine_getPayloadV4";
pub const ENGINE_GET_PAYLOAD_V5: &str = "engine_getPayloadV5";
pub const ENGINE_GET_PAYLOAD_V6: &str = "engine_getPayloadV6";
pub const ENGINE_GET_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(2);
pub const ENGINE_FORKCHOICE_UPDATED_V1: &str = "engine_forkchoiceUpdatedV1";
pub const ENGINE_FORKCHOICE_UPDATED_V2: &str = "engine_forkchoiceUpdatedV2";
pub const ENGINE_FORKCHOICE_UPDATED_V3: &str = "engine_forkchoiceUpdatedV3";
pub const ENGINE_FORKCHOICE_UPDATED_V4: &str = "engine_forkchoiceUpdatedV4";
pub const ENGINE_FORKCHOICE_UPDATED_TIMEOUT: Duration = Duration::from_secs(8);
pub const ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1: &str = "engine_getPayloadBodiesByHashV1";
pub const ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2: &str = "engine_getPayloadBodiesByHashV2";
pub const ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1: &str = "engine_getPayloadBodiesByRangeV1";
pub const ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2: &str = "engine_getPayloadBodiesByRangeV2";
pub const ENGINE_GET_PAYLOAD_BODIES_TIMEOUT: Duration = Duration::from_secs(10);
pub const ENGINE_EXCHANGE_CAPABILITIES: &str = "engine_exchangeCapabilities";
@@ -74,16 +80,21 @@ pub static LIGHTHOUSE_CAPABILITIES: &[&str] = &[
ENGINE_NEW_PAYLOAD_V2,
ENGINE_NEW_PAYLOAD_V3,
ENGINE_NEW_PAYLOAD_V4,
ENGINE_NEW_PAYLOAD_V5,
ENGINE_GET_PAYLOAD_V1,
ENGINE_GET_PAYLOAD_V2,
ENGINE_GET_PAYLOAD_V3,
ENGINE_GET_PAYLOAD_V4,
ENGINE_GET_PAYLOAD_V5,
ENGINE_GET_PAYLOAD_V6,
ENGINE_FORKCHOICE_UPDATED_V1,
ENGINE_FORKCHOICE_UPDATED_V2,
ENGINE_FORKCHOICE_UPDATED_V3,
ENGINE_FORKCHOICE_UPDATED_V4,
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2,
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2,
ENGINE_GET_CLIENT_VERSION_V1,
ENGINE_GET_BLOBS_V1,
ENGINE_GET_BLOBS_V2,
@@ -912,6 +923,69 @@ impl HttpJsonRpc {
Ok(response.into())
}
pub async fn new_payload_v5_gloas<E: EthSpec>(
&self,
new_payload_request_gloas: NewPayloadRequestGloas<'_, E>,
) -> Result<PayloadStatusV1, Error> {
let block_hash = new_payload_request_gloas.execution_payload.block_hash;
let block_number = new_payload_request_gloas.execution_payload.block_number;
let slot_number = new_payload_request_gloas.execution_payload.slot_number;
debug!(
%block_hash,
block_number,
slot_number,
"Sending engine_newPayloadV5 for Gloas"
);
let params = json!([
JsonExecutionPayload::Gloas(
new_payload_request_gloas
.execution_payload
.clone()
.try_into()?
),
new_payload_request_gloas.versioned_hashes,
new_payload_request_gloas.parent_beacon_block_root,
new_payload_request_gloas
.execution_requests
.get_execution_requests_list(),
]);
let result: Result<JsonPayloadStatusV1, _> = self
.rpc_request(
ENGINE_NEW_PAYLOAD_V5,
params,
ENGINE_NEW_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
)
.await;
match &result {
Ok(response) => {
debug!(
%block_hash,
block_number,
slot_number,
status = ?response.status,
latest_valid_hash = ?response.latest_valid_hash,
validation_error = ?response.validation_error,
"Received engine_newPayloadV5 response"
);
}
Err(e) => {
error!(
%block_hash,
block_number,
slot_number,
error = ?e,
"engine_newPayloadV5 failed"
);
}
}
Ok(result?.into())
}
pub async fn get_payload_v1<E: EthSpec>(
&self,
payload_id: PayloadId,
@@ -1067,6 +1141,61 @@ impl HttpJsonRpc {
}
}
pub async fn get_payload_v6<E: EthSpec>(
&self,
fork_name: ForkName,
payload_id: PayloadId,
) -> Result<GetPayloadResponse<E>, Error> {
debug!(
?payload_id,
%fork_name,
"Sending engine_getPayloadV6 for Gloas"
);
let params = json!([JsonPayloadIdRequest::from(payload_id)]);
match fork_name {
ForkName::Gloas => {
let result: Result<JsonGetPayloadResponseGloas<E>, _> = self
.rpc_request(
ENGINE_GET_PAYLOAD_V6,
params,
ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
)
.await;
match &result {
Ok(response) => {
debug!(
?payload_id,
block_hash = %response.execution_payload.block_hash,
block_number = response.execution_payload.block_number,
slot_number = response.execution_payload.slot_number,
block_value = %response.block_value,
"Received engine_getPayloadV6 response"
);
}
Err(e) => {
error!(
?payload_id,
%fork_name,
error = ?e,
"engine_getPayloadV6 failed"
);
}
}
JsonGetPayloadResponse::Gloas(result?)
.try_into()
.map_err(Error::BadResponse)
}
_ => Err(Error::UnsupportedForkVariant(format!(
"called get_payload_v6 with {}",
fork_name
))),
}
}
pub async fn forkchoice_updated_v1(
&self,
forkchoice_state: ForkchoiceState,
@@ -1130,6 +1259,63 @@ impl HttpJsonRpc {
Ok(response.into())
}
pub async fn forkchoice_updated_v4(
&self,
forkchoice_state: ForkchoiceState,
payload_attributes: Option<PayloadAttributes>,
) -> Result<ForkchoiceUpdatedResponse, Error> {
let slot_number = payload_attributes
.as_ref()
.and_then(|pa| pa.slot_number().ok());
debug!(
head_block_hash = %forkchoice_state.head_block_hash,
safe_block_hash = %forkchoice_state.safe_block_hash,
finalized_block_hash = %forkchoice_state.finalized_block_hash,
has_payload_attributes = payload_attributes.is_some(),
?slot_number,
"Sending engine_forkchoiceUpdatedV4 for Gloas"
);
let params = json!([
JsonForkchoiceStateV1::from(forkchoice_state),
payload_attributes.map(JsonPayloadAttributes::from)
]);
let result: Result<JsonForkchoiceUpdatedV1Response, _> = self
.rpc_request(
ENGINE_FORKCHOICE_UPDATED_V4,
params,
ENGINE_FORKCHOICE_UPDATED_TIMEOUT * self.execution_timeout_multiplier,
)
.await;
match &result {
Ok(response) => {
debug!(
head_block_hash = %forkchoice_state.head_block_hash,
status = ?response.payload_status.status,
latest_valid_hash = ?response.payload_status.latest_valid_hash,
payload_id = ?response.payload_id,
?slot_number,
"Received engine_forkchoiceUpdatedV4 response"
);
}
Err(e) => {
error!(
head_block_hash = %forkchoice_state.head_block_hash,
safe_block_hash = %forkchoice_state.safe_block_hash,
finalized_block_hash = %forkchoice_state.finalized_block_hash,
?slot_number,
error = ?e,
"engine_forkchoiceUpdatedV4 failed"
);
}
}
Ok(result?.into())
}
pub async fn get_payload_bodies_by_hash_v1<E: EthSpec>(
&self,
block_hashes: Vec<ExecutionBlockHash>,
@@ -1182,6 +1368,58 @@ impl HttpJsonRpc {
.collect::<Result<Vec<_>, _>>()
}
pub async fn get_payload_bodies_by_hash_v2<E: EthSpec>(
&self,
block_hashes: Vec<ExecutionBlockHash>,
) -> Result<Vec<Option<ExecutionPayloadBodyV2<E>>>, Error> {
let params = json!([block_hashes]);
let response: Vec<Option<JsonExecutionPayloadBodyV2<E>>> = self
.rpc_request(
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2,
params,
ENGINE_GET_PAYLOAD_BODIES_TIMEOUT * self.execution_timeout_multiplier,
)
.await?;
response
.into_iter()
.map(|opt_json| {
opt_json
.map(|json| json.try_into().map_err(Error::from))
.transpose()
})
.collect::<Result<Vec<_>, _>>()
}
pub async fn get_payload_bodies_by_range_v2<E: EthSpec>(
&self,
start: u64,
count: u64,
) -> Result<Vec<Option<ExecutionPayloadBodyV2<E>>>, Error> {
#[derive(Serialize)]
#[serde(transparent)]
struct Quantity(#[serde(with = "serde_utils::u64_hex_be")] u64);
let params = json!([Quantity(start), Quantity(count)]);
let response: Vec<Option<JsonExecutionPayloadBodyV2<E>>> = self
.rpc_request(
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2,
params,
ENGINE_GET_PAYLOAD_BODIES_TIMEOUT * self.execution_timeout_multiplier,
)
.await?;
response
.into_iter()
.map(|opt_json| {
opt_json
.map(|json| json.try_into().map_err(Error::from))
.transpose()
})
.collect::<Result<Vec<_>, _>>()
}
pub async fn exchange_capabilities(&self) -> Result<EngineCapabilities, Error> {
let params = json!([LIGHTHOUSE_CAPABILITIES]);
@@ -1198,18 +1436,25 @@ impl HttpJsonRpc {
new_payload_v2: capabilities.contains(ENGINE_NEW_PAYLOAD_V2),
new_payload_v3: capabilities.contains(ENGINE_NEW_PAYLOAD_V3),
new_payload_v4: capabilities.contains(ENGINE_NEW_PAYLOAD_V4),
new_payload_v5: capabilities.contains(ENGINE_NEW_PAYLOAD_V5),
forkchoice_updated_v1: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V1),
forkchoice_updated_v2: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V2),
forkchoice_updated_v3: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V3),
forkchoice_updated_v4: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V4),
get_payload_bodies_by_hash_v1: capabilities
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1),
get_payload_bodies_by_hash_v2: capabilities
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2),
get_payload_bodies_by_range_v1: capabilities
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1),
get_payload_bodies_by_range_v2: capabilities
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2),
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_v4: capabilities.contains(ENGINE_GET_PAYLOAD_V4),
get_payload_v5: capabilities.contains(ENGINE_GET_PAYLOAD_V5),
get_payload_v6: capabilities.contains(ENGINE_GET_PAYLOAD_V6),
get_client_version_v1: capabilities.contains(ENGINE_GET_CLIENT_VERSION_V1),
get_blobs_v1: capabilities.contains(ENGINE_GET_BLOBS_V1),
get_blobs_v2: capabilities.contains(ENGINE_GET_BLOBS_V2),
@@ -1353,10 +1598,12 @@ impl HttpJsonRpc {
}
}
NewPayloadRequest::Gloas(new_payload_request_gloas) => {
if engine_capabilities.new_payload_v4 {
self.new_payload_v4_gloas(new_payload_request_gloas).await
if engine_capabilities.new_payload_v5 {
self.new_payload_v5_gloas(new_payload_request_gloas).await
} else {
Err(Error::RequiredMethodUnsupported("engine_newPayloadV4"))
Err(Error::RequiredMethodUnsupported(
"engine_newPayloadV5 is required for Gloas/Amsterdam fork",
))
}
}
}
@@ -1402,10 +1649,12 @@ impl HttpJsonRpc {
}
}
ForkName::Gloas => {
if engine_capabilities.get_payload_v5 {
self.get_payload_v5(fork_name, payload_id).await
if engine_capabilities.get_payload_v6 {
self.get_payload_v6(fork_name, payload_id).await
} else {
Err(Error::RequiredMethodUnsupported("engine_getPayloadv5"))
Err(Error::RequiredMethodUnsupported(
"engine_getPayloadV6 is required for Gloas/Amsterdam fork",
))
}
}
ForkName::Base | ForkName::Altair => Err(Error::UnsupportedForkVariant(format!(
@@ -1446,6 +1695,16 @@ impl HttpJsonRpc {
))
}
}
PayloadAttributes::V4(_) => {
if engine_capabilities.forkchoice_updated_v4 {
self.forkchoice_updated_v4(forkchoice_state, maybe_payload_attributes)
.await
} else {
Err(Error::RequiredMethodUnsupported(
"engine_forkchoiceUpdatedV4 is required for Gloas/Amsterdam fork",
))
}
}
}
} else if engine_capabilities.forkchoice_updated_v3 {
self.forkchoice_updated_v3(forkchoice_state, maybe_payload_attributes)

View File

@@ -107,6 +107,12 @@ pub struct JsonExecutionPayload<E: EthSpec> {
#[superstruct(only(Deneb, Electra, Fulu, Gloas))]
#[serde(with = "serde_utils::u64_hex_be")]
pub excess_blob_gas: u64,
#[superstruct(only(Gloas))]
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
pub block_access_list: VariableList<u8, E::MaxBytesPerTransaction>,
#[superstruct(only(Gloas))]
#[serde(with = "serde_utils::u64_hex_be")]
pub slot_number: u64,
}
impl<E: EthSpec> From<ExecutionPayloadBellatrix<E>> for JsonExecutionPayloadBellatrix<E> {
@@ -252,6 +258,8 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadGloas<E>> for JsonExecutionPayloadGloas
withdrawals: withdrawals_to_json(payload.withdrawals)?,
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
block_access_list: payload.block_access_list,
slot_number: payload.slot_number,
})
}
}
@@ -425,6 +433,8 @@ impl<E: EthSpec> TryFrom<JsonExecutionPayloadGloas<E>> for ExecutionPayloadGloas
withdrawals: withdrawals_from_json(payload.withdrawals)?,
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
block_access_list: payload.block_access_list,
slot_number: payload.slot_number,
})
}
}
@@ -716,7 +726,7 @@ impl<'a> From<&'a JsonWithdrawal> for EncodableJsonWithdrawal<'a> {
}
#[superstruct(
variants(V1, V2, V3),
variants(V1, V2, V3, V4),
variant_attributes(
derive(Debug, Clone, PartialEq, Serialize, Deserialize),
serde(rename_all = "camelCase")
@@ -732,10 +742,13 @@ pub struct JsonPayloadAttributes {
pub prev_randao: Hash256,
#[serde(with = "serde_utils::address_hex")]
pub suggested_fee_recipient: Address,
#[superstruct(only(V2, V3))]
#[superstruct(only(V2, V3, V4))]
pub withdrawals: Vec<JsonWithdrawal>,
#[superstruct(only(V3))]
#[superstruct(only(V3, V4))]
pub parent_beacon_block_root: Hash256,
#[superstruct(only(V4))]
#[serde(with = "serde_utils::u64_hex_be")]
pub slot_number: u64,
}
impl From<PayloadAttributes> for JsonPayloadAttributes {
@@ -759,6 +772,14 @@ impl From<PayloadAttributes> for JsonPayloadAttributes {
withdrawals: pa.withdrawals.into_iter().map(Into::into).collect(),
parent_beacon_block_root: pa.parent_beacon_block_root,
}),
PayloadAttributes::V4(pa) => Self::V4(JsonPayloadAttributesV4 {
timestamp: pa.timestamp,
prev_randao: pa.prev_randao,
suggested_fee_recipient: pa.suggested_fee_recipient,
withdrawals: pa.withdrawals.into_iter().map(Into::into).collect(),
parent_beacon_block_root: pa.parent_beacon_block_root,
slot_number: pa.slot_number,
}),
}
}
}
@@ -784,6 +805,14 @@ impl From<JsonPayloadAttributes> for PayloadAttributes {
withdrawals: jpa.withdrawals.into_iter().map(Into::into).collect(),
parent_beacon_block_root: jpa.parent_beacon_block_root,
}),
JsonPayloadAttributes::V4(jpa) => Self::V4(PayloadAttributesV4 {
timestamp: jpa.timestamp,
prev_randao: jpa.prev_randao,
suggested_fee_recipient: jpa.suggested_fee_recipient,
withdrawals: jpa.withdrawals.into_iter().map(Into::into).collect(),
parent_beacon_block_root: jpa.parent_beacon_block_root,
slot_number: jpa.slot_number,
}),
}
}
}
@@ -991,12 +1020,24 @@ impl From<ForkchoiceUpdatedResponse> for JsonForkchoiceUpdatedV1Response {
}
}
#[superstruct(
variants(V1, V2),
variant_attributes(
derive(Clone, Debug, Serialize, Deserialize),
serde(bound = "E: EthSpec", rename_all = "camelCase")
),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct JsonExecutionPayloadBodyV1<E: EthSpec> {
#[serde(bound = "E: EthSpec", rename_all = "camelCase")]
pub struct JsonExecutionPayloadBody<E: EthSpec> {
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: Transactions<E>,
pub withdrawals: Option<VariableList<JsonWithdrawal, E::MaxWithdrawalsPerPayload>>,
#[superstruct(only(V2))]
#[serde(with = "optional_hex_var_list")]
pub block_access_list: Option<VariableList<u8, E::MaxBytesPerTransaction>>,
}
impl<E: EthSpec> TryFrom<JsonExecutionPayloadBodyV1<E>> for ExecutionPayloadBodyV1<E> {
@@ -1021,6 +1062,30 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadBodyV1<E>> for JsonExecutionPayloadBody
}
}
impl<E: EthSpec> TryFrom<JsonExecutionPayloadBodyV2<E>> for ExecutionPayloadBodyV2<E> {
type Error = ssz_types::Error;
fn try_from(value: JsonExecutionPayloadBodyV2<E>) -> Result<Self, Self::Error> {
Ok(Self {
transactions: value.transactions,
withdrawals: value.withdrawals.map(withdrawals_from_json).transpose()?,
block_access_list: value.block_access_list,
})
}
}
impl<E: EthSpec> TryFrom<ExecutionPayloadBodyV2<E>> for JsonExecutionPayloadBodyV2<E> {
type Error = ssz_types::Error;
fn try_from(value: ExecutionPayloadBodyV2<E>) -> Result<Self, Self::Error> {
Ok(Self {
transactions: value.transactions,
withdrawals: value.withdrawals.map(withdrawals_to_json).transpose()?,
block_access_list: value.block_access_list,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransitionConfigurationV1 {
@@ -1061,6 +1126,50 @@ pub mod serde_logs_bloom {
}
}
/// Serializes an optional hex variable list field (e.g., blockAccessList in EIP-7928).
/// JSON `null` maps to `None`, hex string maps to `Some(VariableList<u8, N>)`.
pub mod optional_hex_var_list {
use super::*;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S, N>(
opt_bytes: &Option<VariableList<u8, N>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
N: Unsigned,
{
match opt_bytes {
Some(bytes) => {
let mut hex_string: String = "0x".to_string();
hex_string.push_str(&hex::encode(&bytes[..]));
serializer.serialize_str(&hex_string)
}
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D, N>(deserializer: D) -> Result<Option<VariableList<u8, N>>, D::Error>
where
D: Deserializer<'de>,
N: Unsigned,
{
let opt: Option<String> = Option::deserialize(deserializer)?;
match opt {
Some(hex_str) => {
let hex_str = hex_str.strip_prefix("0x").unwrap_or(&hex_str);
let bytes = hex::decode(hex_str)
.map_err(|e| serde::de::Error::custom(format!("invalid hex: {:?}", e)))?;
VariableList::new(bytes)
.map(Some)
.map_err(|e| serde::de::Error::custom(format!("invalid var list: {:?}", e)))
}
None => Ok(None),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JsonClientVersionV1 {

View File

@@ -221,7 +221,17 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
parent_beacon_block_root: block_ref.parent_root,
execution_requests: &block_ref.body.execution_requests,
})),
BeaconBlockRef::Gloas(_) => Err(Self::Error::IncorrectStateVariant),
BeaconBlockRef::Gloas(block_ref) => Ok(Self::Gloas(NewPayloadRequestGloas {
execution_payload: &block_ref.body.execution_payload.execution_payload,
versioned_hashes: block_ref
.body
.blob_kzg_commitments
.iter()
.map(kzg_commitment_to_versioned_hash)
.collect(),
parent_beacon_block_root: block_ref.parent_root,
execution_requests: &block_ref.body.execution_requests,
})),
}
}
}

View File

@@ -158,6 +158,7 @@ pub enum Error {
},
ZeroLengthTransaction,
PayloadBodiesByRangeNotSupported,
PayloadBodiesByHashNotSupported,
GetBlobsNotSupported,
InvalidJWTSecret(String),
InvalidForkForPayload,
@@ -1787,32 +1788,87 @@ impl<E: EthSpec> ExecutionLayer<E> {
pub async fn get_payload_bodies_by_hash(
&self,
hashes: Vec<ExecutionBlockHash>,
) -> Result<Vec<Option<ExecutionPayloadBodyV1<E>>>, Error> {
self.engine()
.request(|engine: &Engine| async move {
engine.api.get_payload_bodies_by_hash_v1(hashes).await
})
.await
.map_err(Box::new)
.map_err(Error::EngineError)
) -> Result<Vec<Option<ExecutionPayloadBody<E>>>, Error> {
let capabilities = self.get_engine_capabilities(None).await?;
if capabilities.get_payload_bodies_by_hash_v2 {
self.engine()
.request(|engine: &Engine| async move {
engine.api.get_payload_bodies_by_hash_v2(hashes).await
})
.await
.map(|bodies| {
bodies
.into_iter()
.map(|opt| opt.map(ExecutionPayloadBody::V2))
.collect()
})
.map_err(Box::new)
.map_err(Error::EngineError)
} else if capabilities.get_payload_bodies_by_hash_v1 {
self.engine()
.request(|engine: &Engine| async move {
engine.api.get_payload_bodies_by_hash_v1::<E>(hashes).await
})
.await
.map(|bodies| {
bodies
.into_iter()
.map(|opt| opt.map(ExecutionPayloadBody::V1))
.collect()
})
.map_err(Box::new)
.map_err(Error::EngineError)
} else {
Err(Error::PayloadBodiesByHashNotSupported)
}
}
pub async fn get_payload_bodies_by_range(
&self,
start: u64,
count: u64,
) -> Result<Vec<Option<ExecutionPayloadBodyV1<E>>>, Error> {
) -> Result<Vec<Option<ExecutionPayloadBody<E>>>, Error> {
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_GET_PAYLOAD_BODIES_BY_RANGE);
self.engine()
.request(|engine: &Engine| async move {
engine
.api
.get_payload_bodies_by_range_v1(start, count)
.await
})
.await
.map_err(Box::new)
.map_err(Error::EngineError)
let capabilities = self.get_engine_capabilities(None).await?;
if capabilities.get_payload_bodies_by_range_v2 {
self.engine()
.request(|engine: &Engine| async move {
engine
.api
.get_payload_bodies_by_range_v2(start, count)
.await
})
.await
.map(|bodies| {
bodies
.into_iter()
.map(|opt| opt.map(ExecutionPayloadBody::V2))
.collect()
})
.map_err(Box::new)
.map_err(Error::EngineError)
} else if capabilities.get_payload_bodies_by_range_v1 {
self.engine()
.request(|engine: &Engine| async move {
engine
.api
.get_payload_bodies_by_range_v1::<E>(start, count)
.await
})
.await
.map(|bodies| {
bodies
.into_iter()
.map(|opt| opt.map(ExecutionPayloadBody::V1))
.collect()
})
.map_err(Box::new)
.map_err(Error::EngineError)
} else {
Err(Error::PayloadBodiesByRangeNotSupported)
}
}
/// Fetch a full payload from the execution node.
@@ -1845,7 +1901,9 @@ impl<E: EthSpec> ExecutionLayer<E> {
// Use efficient payload bodies by range method if supported.
let capabilities = self.get_engine_capabilities(None).await?;
if capabilities.get_payload_bodies_by_range_v1 {
if capabilities.get_payload_bodies_by_range_v2
|| capabilities.get_payload_bodies_by_range_v1
{
let mut payload_bodies = self.get_payload_bodies_by_range(block_number, 1).await?;
if payload_bodies.len() != 1 {

View File

@@ -707,6 +707,9 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
blob_gas_used: 0,
excess_blob_gas: 0,
}),
_ => unreachable!(),
},
PayloadAttributes::V4(pa) => match self.get_fork_at_timestamp(pa.timestamp) {
ForkName::Gloas => ExecutionPayload::Gloas(ExecutionPayloadGloas {
parent_hash: head_block_hash,
fee_recipient: pa.suggested_fee_recipient,
@@ -725,6 +728,8 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
withdrawals: pa.withdrawals.clone().try_into().unwrap(),
blob_gas_used: 0,
excess_blob_gas: 0,
block_access_list: VariableList::empty(),
slot_number: pa.slot_number,
}),
_ => unreachable!(),
},

View File

@@ -117,6 +117,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.fee_recipient = fee_recipient;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.fee_recipient = fee_recipient;
}
}
}
@@ -137,6 +140,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.gas_limit = gas_limit;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.gas_limit = gas_limit;
}
}
}
@@ -161,6 +167,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
}
}
}
@@ -181,6 +190,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.prev_randao = prev_randao;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.prev_randao = prev_randao;
}
}
}
@@ -201,6 +213,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.block_number = block_number;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.block_number = block_number;
}
}
}
@@ -221,6 +236,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.timestamp = timestamp;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.timestamp = timestamp;
}
}
}
@@ -241,6 +259,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.withdrawals_root = withdrawals_root;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.withdrawals_root = withdrawals_root;
}
}
}
@@ -274,6 +295,10 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
header.extra_data = extra_data;
header.block_hash = ExecutionBlockHash::from_root(header.tree_hash_root());
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.extra_data = extra_data;
header.block_hash = ExecutionBlockHash::from_root(header.tree_hash_root());
}
}
}
}
@@ -897,16 +922,24 @@ impl<E: EthSpec> MockBuilder<E> {
fee_recipient,
expected_withdrawals,
None,
None,
),
ForkName::Deneb | ForkName::Electra | ForkName::Fulu => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
None,
),
ForkName::Gloas => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
Some(slot.as_u64()),
),
ForkName::Deneb | ForkName::Electra | ForkName::Fulu | ForkName::Gloas => {
PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
)
}
ForkName::Base | ForkName::Altair => {
return Err("invalid fork".to_string());
}

View File

@@ -107,8 +107,14 @@ impl<E: EthSpec> MockExecutionLayer<E> {
justified_hash: None,
finalized_hash: None,
};
let payload_attributes =
PayloadAttributes::new(timestamp, prev_randao, Address::repeat_byte(42), None, None);
let payload_attributes = PayloadAttributes::new(
timestamp,
prev_randao,
Address::repeat_byte(42),
None,
None,
None,
);
// Insert a proposer to ensure the fork choice updated command works.
let slot = Slot::new(0);
@@ -135,8 +141,14 @@ impl<E: EthSpec> MockExecutionLayer<E> {
chain_health: ChainHealth::Healthy,
};
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
let payload_attributes =
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
let payload_attributes = PayloadAttributes::new(
timestamp,
prev_randao,
suggested_fee_recipient,
None,
None,
None,
);
let payload_parameters = PayloadParameters {
parent_hash,
@@ -182,8 +194,14 @@ impl<E: EthSpec> MockExecutionLayer<E> {
chain_health: ChainHealth::Healthy,
};
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
let payload_attributes =
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
let payload_attributes = PayloadAttributes::new(
timestamp,
prev_randao,
suggested_fee_recipient,
None,
None,
None,
);
let payload_parameters = PayloadParameters {
parent_hash,

View File

@@ -45,16 +45,21 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
new_payload_v2: true,
new_payload_v3: true,
new_payload_v4: true,
new_payload_v5: true,
forkchoice_updated_v1: true,
forkchoice_updated_v2: true,
forkchoice_updated_v3: true,
forkchoice_updated_v4: true,
get_payload_bodies_by_hash_v1: true,
get_payload_bodies_by_hash_v2: true,
get_payload_bodies_by_range_v1: true,
get_payload_bodies_by_range_v2: true,
get_payload_v1: true,
get_payload_v2: true,
get_payload_v3: true,
get_payload_v4: true,
get_payload_v5: true,
get_payload_v6: true,
get_client_version_v1: true,
get_blobs_v1: true,
get_blobs_v2: true,

View File

@@ -456,6 +456,12 @@ pub fn process_execution_payload<E: EthSpec, Payload: AbstractExecPayload<E>>(
_ => return Err(BlockProcessingError::IncorrectStateType),
}
}
ExecutionPayloadHeaderRefMut::Gloas(header_mut) => {
match payload.to_execution_payload_header() {
ExecutionPayloadHeader::Gloas(header) => *header_mut = header,
_ => return Err(BlockProcessingError::IncorrectStateType),
}
}
}
Ok(())

View File

@@ -1,11 +1,5 @@
use bls::Hash256;
use milhouse::{List, Vector};
use ssz_types::BitVector;
use std::mem;
use types::{
BeaconState, BeaconStateError as Error, BeaconStateGloas, BuilderPendingPayment, ChainSpec,
EthSpec, ExecutionPayloadBid, Fork,
};
use types::{BeaconState, BeaconStateError as Error, BeaconStateGloas, ChainSpec, EthSpec, Fork};
/// Transform a `Fulu` state into a `Gloas` state.
pub fn upgrade_to_gloas<E: EthSpec>(
@@ -69,11 +63,10 @@ pub fn upgrade_state_to_gloas<E: EthSpec>(
// Sync committees
current_sync_committee: pre.current_sync_committee.clone(),
next_sync_committee: pre.next_sync_committee.clone(),
// Execution Bid
latest_execution_payload_bid: ExecutionPayloadBid {
block_hash: pre.latest_execution_payload_header.block_hash,
..Default::default()
},
// Execution - upgrade header from Fulu to Gloas
latest_execution_payload_header: pre
.latest_execution_payload_header
.upgrade_to_gloas(pre.latest_block_header.slot.into()),
// Capella
next_withdrawal_index: pre.next_withdrawal_index,
next_withdrawal_validator_index: pre.next_withdrawal_validator_index,
@@ -88,15 +81,6 @@ pub fn upgrade_state_to_gloas<E: EthSpec>(
pending_deposits: pre.pending_deposits.clone(),
pending_partial_withdrawals: pre.pending_partial_withdrawals.clone(),
pending_consolidations: pre.pending_consolidations.clone(),
// Gloas
execution_payload_availability: BitVector::default(), // All bits set to false initially
builder_pending_payments: Vector::new(vec![
BuilderPendingPayment::default();
E::builder_pending_payments_limit()
])?,
builder_pending_withdrawals: List::default(), // Empty list initially,
latest_block_hash: pre.latest_execution_payload_header.block_hash,
latest_withdrawals_root: Hash256::default(),
// Caches
total_active_balance: pre.total_active_balance,
progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache),

View File

@@ -15,7 +15,6 @@ use tree_hash_derive::TreeHash;
use typenum::Unsigned;
use crate::{
SignedExecutionPayloadBid,
attestation::{AttestationBase, AttestationData, IndexedAttestationBase},
block::{
BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyBellatrix,
@@ -695,41 +694,15 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockGloa
deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(),
sync_aggregate: SyncAggregate::empty(),
execution_payload: Payload::Gloas::default(),
bls_to_execution_changes: VariableList::empty(),
signed_execution_payload_bid: SignedExecutionPayloadBid::empty(),
payload_attestations: VariableList::empty(),
_phantom: PhantomData,
blob_kzg_commitments: VariableList::empty(),
execution_requests: ExecutionRequests::default(),
},
}
}
}
// TODO(EIP-7732) Mark's branch had the following implementation but not sure if it's needed so will just add header below for reference
// impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockEIP7732<E, Payload> {
// TODO(EIP-7732) Look into whether we can remove this in the future since no blinded blocks post-gloas
impl<E: EthSpec> From<BeaconBlockGloas<E, BlindedPayload<E>>>
for BeaconBlockGloas<E, FullPayload<E>>
{
fn from(block: BeaconBlockGloas<E, BlindedPayload<E>>) -> Self {
let BeaconBlockGloas {
slot,
proposer_index,
parent_root,
state_root,
body,
} = block;
BeaconBlockGloas {
slot,
proposer_index,
parent_root,
state_root,
body: body.into(),
}
}
}
// We can convert pre-Bellatrix blocks without payloads into blocks "with" payloads.
impl<E: EthSpec> From<BeaconBlockBase<E, BlindedPayload<E>>>
for BeaconBlockBase<E, FullPayload<E>>

View File

@@ -14,19 +14,17 @@ use tree_hash::{BYTES_PER_CHUNK, TreeHash};
use tree_hash_derive::TreeHash;
use crate::{
SignedExecutionPayloadBid,
attestation::{
AttestationBase, AttestationElectra, AttestationRef, AttestationRefMut, PayloadAttestation,
},
attestation::{AttestationBase, AttestationElectra, AttestationRef, AttestationRefMut},
core::{EthSpec, Graffiti, Hash256},
deposit::Deposit,
execution::{
AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella,
BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, Eth1Data, ExecutionPayload,
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionRequests,
FullPayload, FullPayloadBellatrix, FullPayloadCapella, FullPayloadDeneb,
FullPayloadElectra, FullPayloadFulu, SignedBlsToExecutionChange,
BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadGloas,
Eth1Data, ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella,
ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
ExecutionPayloadGloas, ExecutionRequests, FullPayload, FullPayloadBellatrix,
FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu,
FullPayloadGloas, SignedBlsToExecutionChange,
},
exit::SignedVoluntaryExit,
fork::{ForkName, map_fork_name},
@@ -159,18 +157,17 @@ pub struct BeaconBlockBody<E: EthSpec, Payload: AbstractExecPayload<E> = FullPay
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
#[serde(flatten)]
pub execution_payload: Payload::Fulu,
#[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))]
#[serde(flatten)]
pub execution_payload: Payload::Gloas,
#[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas))]
pub bls_to_execution_changes:
VariableList<SignedBlsToExecutionChange, E::MaxBlsToExecutionChanges>,
#[superstruct(only(Deneb, Electra, Fulu))]
#[superstruct(only(Deneb, Electra, Fulu, Gloas))]
pub blob_kzg_commitments: KzgCommitments<E>,
#[superstruct(only(Electra, Fulu))]
#[superstruct(only(Electra, Fulu, Gloas))]
pub execution_requests: ExecutionRequests<E>,
#[superstruct(only(Gloas))]
pub signed_execution_payload_bid: SignedExecutionPayloadBid,
#[superstruct(only(Gloas))]
pub payload_attestations: VariableList<PayloadAttestation<E>, E::MaxPayloadAttestations>,
#[superstruct(only(Base, Altair, Gloas))]
#[superstruct(only(Base, Altair))]
#[metastruct(exclude_from(fields))]
#[ssz(skip_serializing, skip_deserializing)]
#[tree_hash(skip_hashing)]
@@ -199,7 +196,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
Self::Deneb(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Electra(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Fulu(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Gloas(_) => Err(BeaconStateError::IncorrectStateVariant),
Self::Gloas(body) => Ok(Payload::Ref::from(&body.execution_payload)),
}
}
@@ -257,19 +254,16 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
/// Produces the proof of inclusion for a `KzgCommitment` in `self.blob_kzg_commitments`
/// at `index` using an existing proof for the `blob_kzg_commitments` field.
/// TODO(EIP7732) Investigate calling functions since this will no longer work for glas since no block_kzg_commitments in the body anymore
pub fn complete_kzg_commitment_merkle_proof(
&self,
index: usize,
kzg_commitments_proof: &[Hash256],
) -> Result<FixedVector<Hash256, E::KzgCommitmentInclusionProofDepth>, BeaconStateError> {
match self {
Self::Base(_)
| Self::Altair(_)
| Self::Bellatrix(_)
| Self::Capella(_)
| Self::Gloas(_) => Err(BeaconStateError::IncorrectStateVariant),
Self::Deneb(_) | Self::Electra(_) | Self::Fulu(_) => {
Self::Base(_) | Self::Altair(_) | Self::Bellatrix(_) | Self::Capella(_) => {
Err(BeaconStateError::IncorrectStateVariant)
}
Self::Deneb(_) | Self::Electra(_) | Self::Fulu(_) | Self::Gloas(_) => {
// We compute the branches by generating 2 merkle trees:
// 1. Merkle tree for the `blob_kzg_commitments` List object
// 2. Merkle tree for the `BeaconBlockBody` container
@@ -547,46 +541,6 @@ impl<E: EthSpec> From<BeaconBlockBodyAltair<E, BlindedPayload<E>>>
}
}
// Post-Fulu block bodies without payloads can be converted into block bodies with payloads
// TODO(EIP-7732) Look into whether we can remove this in the future since no blinded blocks post-gloas
impl<E: EthSpec> From<BeaconBlockBodyGloas<E, BlindedPayload<E>>>
for BeaconBlockBodyGloas<E, FullPayload<E>>
{
fn from(body: BeaconBlockBodyGloas<E, BlindedPayload<E>>) -> Self {
let BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
bls_to_execution_changes,
signed_execution_payload_bid,
payload_attestations,
_phantom,
} = body;
BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
bls_to_execution_changes,
signed_execution_payload_bid,
payload_attestations,
_phantom: PhantomData,
}
}
}
// Likewise bodies with payloads can be transformed into bodies without.
impl<E: EthSpec> From<BeaconBlockBodyBase<E, FullPayload<E>>>
for (
@@ -897,10 +851,10 @@ impl<E: EthSpec> From<BeaconBlockBodyGloas<E, FullPayload<E>>>
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadGloas { execution_payload },
bls_to_execution_changes,
signed_execution_payload_bid,
payload_attestations,
_phantom,
blob_kzg_commitments,
execution_requests,
} = body;
(
@@ -914,12 +868,14 @@ impl<E: EthSpec> From<BeaconBlockBodyGloas<E, FullPayload<E>>>
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadGloas {
execution_payload_header: From::from(&execution_payload),
},
bls_to_execution_changes,
signed_execution_payload_bid,
payload_attestations,
_phantom: PhantomData,
blob_kzg_commitments: blob_kzg_commitments.clone(),
execution_requests,
},
None,
Some(execution_payload),
)
}
}

View File

@@ -17,17 +17,19 @@ use crate::{
block::{
BLOB_KZG_COMMITMENTS_INDEX, BeaconBlock, BeaconBlockAltair, BeaconBlockBase,
BeaconBlockBellatrix, BeaconBlockBodyBellatrix, BeaconBlockBodyCapella,
BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu, BeaconBlockCapella,
BeaconBlockDeneb, BeaconBlockElectra, BeaconBlockFulu, BeaconBlockGloas, BeaconBlockHeader,
BeaconBlockRef, BeaconBlockRefMut, SignedBeaconBlockHeader,
BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu, BeaconBlockBodyGloas,
BeaconBlockCapella, BeaconBlockDeneb, BeaconBlockElectra, BeaconBlockFulu,
BeaconBlockGloas, BeaconBlockHeader, BeaconBlockRef, BeaconBlockRefMut,
SignedBeaconBlockHeader,
},
core::{ChainSpec, Domain, Epoch, EthSpec, Hash256, SignedRoot, SigningData, Slot},
execution::{
AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella,
BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, ExecutionPayload,
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
ExecutionPayloadElectra, ExecutionPayloadFulu, FullPayload, FullPayloadBellatrix,
FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu,
BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadGloas,
ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella,
ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
ExecutionPayloadGloas, FullPayload, FullPayloadBellatrix, FullPayloadCapella,
FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu, FullPayloadGloas,
},
fork::{Fork, ForkName, ForkVersionDecode, InconsistentFork, map_fork_name},
kzg_ext::format_kzg_commitments,
@@ -673,15 +675,59 @@ impl<E: EthSpec> SignedBeaconBlockFulu<E, BlindedPayload<E>> {
}
}
// We can convert gloas blocks without payloads into blocks "with" payloads.
// TODO(EIP-7732) Look into whether we can remove this in the future since no blinded blocks post-gloas
impl<E: EthSpec> From<SignedBeaconBlockGloas<E, BlindedPayload<E>>>
for SignedBeaconBlockGloas<E, FullPayload<E>>
{
fn from(signed_block: SignedBeaconBlockGloas<E, BlindedPayload<E>>) -> Self {
let SignedBeaconBlockGloas { message, signature } = signed_block;
impl<E: EthSpec> SignedBeaconBlockGloas<E, BlindedPayload<E>> {
pub fn into_full_block(
self,
execution_payload: ExecutionPayloadGloas<E>,
) -> SignedBeaconBlockGloas<E, FullPayload<E>> {
let SignedBeaconBlockGloas {
message:
BeaconBlockGloas {
slot,
proposer_index,
parent_root,
state_root,
body:
BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadGloas { .. },
bls_to_execution_changes,
blob_kzg_commitments,
execution_requests,
},
},
signature,
} = self;
SignedBeaconBlockGloas {
message: message.into(),
message: BeaconBlockGloas {
slot,
proposer_index,
parent_root,
state_root,
body: BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadGloas { execution_payload },
bls_to_execution_changes,
blob_kzg_commitments,
execution_requests,
},
},
signature,
}
}
@@ -710,7 +756,9 @@ impl<E: EthSpec> SignedBeaconBlock<E, BlindedPayload<E>> {
(SignedBeaconBlock::Fulu(block), Some(ExecutionPayload::Fulu(payload))) => {
SignedBeaconBlock::Fulu(block.into_full_block(payload))
}
(SignedBeaconBlock::Gloas(block), _) => SignedBeaconBlock::Gloas(block.into()),
(SignedBeaconBlock::Gloas(block), Some(ExecutionPayload::Gloas(payload))) => {
SignedBeaconBlock::Gloas(block.into_full_block(payload))
}
// avoid wildcard matching forks so that compiler will
// direct us here when a new fork has been added
(SignedBeaconBlock::Bellatrix(_), _) => return None,
@@ -718,7 +766,7 @@ impl<E: EthSpec> SignedBeaconBlock<E, BlindedPayload<E>> {
(SignedBeaconBlock::Deneb(_), _) => return None,
(SignedBeaconBlock::Electra(_), _) => return None,
(SignedBeaconBlock::Fulu(_), _) => return None,
// TODO(EIP-7732) Determine if need a match arm for gloas here
(SignedBeaconBlock::Gloas(_), _) => return None,
};
Some(full_block)
}

View File

@@ -3,7 +3,6 @@
// - ExecutionPayloadHeader
// - FullPayload
// - BlindedPayload
// TODO(EIP-7732): get rid of this whole file and panics once the engine_api is refactored for ePBS
#[macro_export]
macro_rules! map_execution_payload_into_full_payload {
@@ -29,7 +28,10 @@ macro_rules! map_execution_payload_into_full_payload {
let f: fn(ExecutionPayloadFulu<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Fulu)
}
ExecutionPayload::Gloas(_) => panic!("FullPayload::Gloas does not exist!"),
ExecutionPayload::Gloas(inner) => {
let f: fn(ExecutionPayloadGloas<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Gloas)
}
}
};
}
@@ -58,7 +60,10 @@ macro_rules! map_execution_payload_into_blinded_payload {
let f: fn(ExecutionPayloadFulu<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Fulu)
}
ExecutionPayload::Gloas(_) => panic!("BlindedPayload::Gloas does not exist!"),
ExecutionPayload::Gloas(inner) => {
let f: fn(ExecutionPayloadGloas<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Gloas)
}
}
};
}
@@ -102,7 +107,13 @@ macro_rules! map_execution_payload_ref_into_execution_payload_header {
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Fulu)
}
ExecutionPayloadRef::Gloas(_) => panic!("ExecutionPayloadHeader::Gloas does not exist!"),
ExecutionPayloadRef::Gloas(inner) => {
let f: fn(
&$lifetime ExecutionPayloadGloas<_>,
fn(_) -> _,
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Gloas)
}
}
}
}

View File

@@ -58,6 +58,8 @@ pub struct ExecutionBlockHeader {
pub excess_blob_gas: Option<u64>,
pub parent_beacon_block_root: Option<Hash256>,
pub requests_root: Option<Hash256>,
pub block_access_list_root: Option<Hash256>,
pub slot_number: Option<u64>,
}
impl ExecutionBlockHeader {
@@ -71,6 +73,8 @@ impl ExecutionBlockHeader {
rlp_excess_blob_gas: Option<u64>,
rlp_parent_beacon_block_root: Option<Hash256>,
rlp_requests_root: Option<Hash256>,
rlp_block_access_list_root: Option<Hash256>,
rlp_slot_number: Option<u64>,
) -> Self {
// Most of these field mappings are defined in EIP-3675 except for `mixHash`, which is
// defined in EIP-4399.
@@ -96,6 +100,8 @@ impl ExecutionBlockHeader {
excess_blob_gas: rlp_excess_blob_gas,
parent_beacon_block_root: rlp_parent_beacon_block_root,
requests_root: rlp_requests_root,
block_access_list_root: rlp_block_access_list_root,
slot_number: rlp_slot_number,
}
}
}
@@ -124,6 +130,8 @@ pub struct EncodableExecutionBlockHeader<'a> {
pub excess_blob_gas: Option<u64>,
pub parent_beacon_block_root: Option<&'a [u8]>,
pub requests_root: Option<&'a [u8]>,
pub block_access_list_root: Option<&'a [u8]>,
pub slot_number: Option<u64>,
}
impl<'a> From<&'a ExecutionBlockHeader> for EncodableExecutionBlockHeader<'a> {
@@ -150,6 +158,8 @@ impl<'a> From<&'a ExecutionBlockHeader> for EncodableExecutionBlockHeader<'a> {
excess_blob_gas: header.excess_blob_gas,
parent_beacon_block_root: None,
requests_root: None,
block_access_list_root: None,
slot_number: header.slot_number,
};
if let Some(withdrawals_root) = &header.withdrawals_root {
encodable.withdrawals_root = Some(withdrawals_root.as_slice());
@@ -160,6 +170,9 @@ impl<'a> From<&'a ExecutionBlockHeader> for EncodableExecutionBlockHeader<'a> {
if let Some(requests_root) = &header.requests_root {
encodable.requests_root = Some(requests_root.as_slice())
}
if let Some(block_access_list_root) = &header.block_access_list_root {
encodable.block_access_list_root = Some(block_access_list_root.as_slice())
}
encodable
}
}

View File

@@ -109,6 +109,14 @@ pub struct ExecutionPayload<E: EthSpec> {
#[superstruct(only(Deneb, Electra, Fulu, Gloas), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub excess_blob_gas: u64,
/// EIP-7928: Block access list
#[superstruct(only(Gloas))]
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
pub block_access_list: VariableList<u8, E::MaxBytesPerTransaction>,
/// EIP-7843: Slot number
#[superstruct(only(Gloas), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub slot_number: u64,
}
impl<'a, E: EthSpec> ExecutionPayloadRef<'a, E> {

View File

@@ -14,7 +14,8 @@ use crate::{
core::{Address, EthSpec, ExecutionBlockHash, Hash256, Uint256},
execution::{
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadRef, Transactions,
ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionPayloadRef,
Transactions,
},
fork::ForkName,
map_execution_payload_ref_into_execution_payload_header,
@@ -23,7 +24,7 @@ use crate::{
};
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas),
variant_attributes(
derive(
Default,
@@ -105,14 +106,21 @@ pub struct ExecutionPayloadHeader<E: EthSpec> {
pub block_hash: ExecutionBlockHash,
#[superstruct(getter(copy))]
pub transactions_root: Hash256,
#[superstruct(only(Capella, Deneb, Electra, Fulu), partial_getter(copy))]
#[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas), partial_getter(copy))]
pub withdrawals_root: Hash256,
#[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))]
#[superstruct(only(Deneb, Electra, Fulu, Gloas), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub blob_gas_used: u64,
#[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))]
#[superstruct(only(Deneb, Electra, Fulu, Gloas), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub excess_blob_gas: u64,
/// EIP-7928: Block access list root
#[superstruct(only(Gloas), partial_getter(copy))]
pub block_access_list_root: Hash256,
/// EIP-7843: Slot number
#[superstruct(only(Gloas), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub slot_number: u64,
}
impl<E: EthSpec> ExecutionPayloadHeader<E> {
@@ -136,19 +144,14 @@ impl<E: EthSpec> ExecutionPayloadHeader<E> {
ExecutionPayloadHeaderElectra::from_ssz_bytes(bytes).map(Self::Electra)
}
ForkName::Fulu => ExecutionPayloadHeaderFulu::from_ssz_bytes(bytes).map(Self::Fulu),
ForkName::Gloas => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayloadHeader: {fork_name}",
))),
ForkName::Gloas => ExecutionPayloadHeaderGloas::from_ssz_bytes(bytes).map(Self::Gloas),
}
}
#[allow(clippy::arithmetic_side_effects)]
pub fn ssz_max_var_len_for_fork(fork_name: ForkName) -> usize {
// TODO(newfork): Add a new case here if there are new variable fields
if fork_name.gloas_enabled() {
// TODO(EIP7732): check this
0
} else if fork_name.bellatrix_enabled() {
if fork_name.bellatrix_enabled() {
// Max size of variable length `extra_data` field
E::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len()
} else {
@@ -163,6 +166,7 @@ impl<E: EthSpec> ExecutionPayloadHeader<E> {
ExecutionPayloadHeader::Deneb(_) => ForkName::Deneb,
ExecutionPayloadHeader::Electra(_) => ForkName::Electra,
ExecutionPayloadHeader::Fulu(_) => ForkName::Fulu,
ExecutionPayloadHeader::Gloas(_) => ForkName::Gloas,
}
}
}
@@ -270,6 +274,32 @@ impl<E: EthSpec> ExecutionPayloadHeaderElectra<E> {
}
}
impl<E: EthSpec> ExecutionPayloadHeaderFulu<E> {
pub fn upgrade_to_gloas(&self, slot_number: u64) -> ExecutionPayloadHeaderGloas<E> {
ExecutionPayloadHeaderGloas {
parent_hash: self.parent_hash,
fee_recipient: self.fee_recipient,
state_root: self.state_root,
receipts_root: self.receipts_root,
logs_bloom: self.logs_bloom.clone(),
prev_randao: self.prev_randao,
block_number: self.block_number,
gas_limit: self.gas_limit,
gas_used: self.gas_used,
timestamp: self.timestamp,
extra_data: self.extra_data.clone(),
base_fee_per_gas: self.base_fee_per_gas,
block_hash: self.block_hash,
transactions_root: self.transactions_root,
withdrawals_root: self.withdrawals_root,
blob_gas_used: self.blob_gas_used,
excess_blob_gas: self.excess_blob_gas,
block_access_list_root: Hash256::zero(),
slot_number,
}
}
}
impl<'a, E: EthSpec> From<&'a ExecutionPayloadBellatrix<E>> for ExecutionPayloadHeaderBellatrix<E> {
fn from(payload: &'a ExecutionPayloadBellatrix<E>) -> Self {
Self {
@@ -385,6 +415,32 @@ impl<'a, E: EthSpec> From<&'a ExecutionPayloadFulu<E>> for ExecutionPayloadHeade
}
}
impl<'a, E: EthSpec> From<&'a ExecutionPayloadGloas<E>> for ExecutionPayloadHeaderGloas<E> {
fn from(payload: &'a ExecutionPayloadGloas<E>) -> Self {
Self {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom.clone(),
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data.clone(),
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: payload.transactions.tree_hash_root(),
withdrawals_root: payload.withdrawals.tree_hash_root(),
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
block_access_list_root: payload.block_access_list.tree_hash_root(),
slot_number: payload.slot_number,
}
}
}
// These impls are required to work around an inelegance in `to_execution_payload_header`.
// They only clone headers so they should be relatively cheap.
impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderBellatrix<E> {
@@ -417,6 +473,12 @@ impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderFulu<E> {
}
}
impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderGloas<E> {
fn from(payload: &'a Self) -> Self {
payload.clone()
}
}
impl<'a, E: EthSpec> From<ExecutionPayloadRef<'a, E>> for ExecutionPayloadHeader<E> {
fn from(payload: ExecutionPayloadRef<'a, E>) -> Self {
map_execution_payload_ref_into_execution_payload_header!(
@@ -478,6 +540,9 @@ impl<E: EthSpec> ExecutionPayloadHeaderRefMut<'_, E> {
ExecutionPayloadHeaderRefMut::Fulu(mut_ref) => {
*mut_ref = header.try_into()?;
}
ExecutionPayloadHeaderRefMut::Gloas(mut_ref) => {
*mut_ref = header.try_into()?;
}
}
Ok(())
}
@@ -505,6 +570,16 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for ExecutionPayloadHeaderFu
}
}
impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for ExecutionPayloadHeaderGloas<E> {
type Error = BeaconStateError;
fn try_from(header: ExecutionPayloadHeader<E>) -> Result<Self, Self::Error> {
match header {
ExecutionPayloadHeader::Gloas(execution_payload_header) => Ok(execution_payload_header),
_ => Err(BeaconStateError::IncorrectStateVariant),
}
}
}
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadHeader<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
@@ -532,8 +607,11 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadHead
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Gloas => {
Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Base | ForkName::Altair | ForkName::Gloas => {
ForkName::Base | ForkName::Altair => {
return Err(serde::de::Error::custom(format!(
"ExecutionPayloadHeader failed to deserialize: unsupported fork '{}'",
context

View File

@@ -26,17 +26,17 @@ pub use execution_payload_envelope::ExecutionPayloadEnvelope;
pub use execution_payload_header::{
ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu,
ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
};
pub use execution_requests::{
ConsolidationRequests, DepositRequests, ExecutionRequests, RequestType, WithdrawalRequests,
};
pub use payload::{
AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella,
BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadRef,
BlockProductionVersion, BlockType, ExecPayload, FullPayload, FullPayloadBellatrix,
FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu, FullPayloadRef,
OwnedExecPayload,
BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadGloas,
BlindedPayloadRef, BlockProductionVersion, BlockType, ExecPayload, FullPayload,
FullPayloadBellatrix, FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra,
FullPayloadFulu, FullPayloadGloas, FullPayloadRef, OwnedExecPayload,
};
pub use signed_bls_to_execution_change::SignedBlsToExecutionChange;
pub use signed_execution_payload_bid::SignedExecutionPayloadBid;

View File

@@ -15,9 +15,9 @@ use crate::{
execution::{
ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella,
ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu,
ExecutionPayloadRef, Transactions,
ExecutionPayloadGloas, ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix,
ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra,
ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderGloas, ExecutionPayloadRef, Transactions,
},
fork::ForkName,
map_execution_payload_into_blinded_payload, map_execution_payload_into_full_payload,
@@ -119,6 +119,7 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ TryInto<Self::Deneb>
+ TryInto<Self::Electra>
+ TryInto<Self::Fulu>
+ TryInto<Self::Gloas>
+ Sync
{
type Ref<'a>: ExecPayload<E>
@@ -127,7 +128,8 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ From<&'a Self::Capella>
+ From<&'a Self::Deneb>
+ From<&'a Self::Electra>
+ From<&'a Self::Fulu>;
+ From<&'a Self::Fulu>
+ From<&'a Self::Gloas>;
type Bellatrix: OwnedExecPayload<E>
+ Into<Self>
@@ -154,10 +156,15 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ for<'a> From<Cow<'a, ExecutionPayloadFulu<E>>>
+ TryFrom<ExecutionPayloadHeaderFulu<E>>
+ Sync;
type Gloas: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadGloas<E>>>
+ TryFrom<ExecutionPayloadHeaderGloas<E>>
+ Sync;
}
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas),
variant_attributes(
derive(
Debug,
@@ -218,6 +225,8 @@ pub struct FullPayload<E: EthSpec> {
pub execution_payload: ExecutionPayloadElectra<E>,
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
pub execution_payload: ExecutionPayloadFulu<E>,
#[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))]
pub execution_payload: ExecutionPayloadGloas<E>,
}
impl<E: EthSpec> From<FullPayload<E>> for ExecutionPayload<E> {
@@ -329,6 +338,7 @@ impl<E: EthSpec> ExecPayload<E> for FullPayload<E> {
FullPayload::Deneb(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Electra(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Fulu(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Gloas(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
}
}
@@ -340,6 +350,7 @@ impl<E: EthSpec> ExecPayload<E> for FullPayload<E> {
FullPayload::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Electra(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Fulu(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Gloas(inner) => Ok(inner.execution_payload.blob_gas_used),
}
}
@@ -371,7 +382,7 @@ impl<E: EthSpec> FullPayload<E> {
ForkName::Deneb => Ok(FullPayloadDeneb::default().into()),
ForkName::Electra => Ok(FullPayloadElectra::default().into()),
ForkName::Fulu => Ok(FullPayloadFulu::default().into()),
ForkName::Gloas => Err(BeaconStateError::IncorrectStateVariant),
ForkName::Gloas => Ok(FullPayloadGloas::default().into()),
}
}
}
@@ -472,6 +483,9 @@ impl<E: EthSpec> ExecPayload<E> for FullPayloadRef<'_, E> {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayloadRef::Fulu(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayloadRef::Gloas(inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
}
}
@@ -483,6 +497,7 @@ impl<E: EthSpec> ExecPayload<E> for FullPayloadRef<'_, E> {
FullPayloadRef::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayloadRef::Electra(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayloadRef::Fulu(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayloadRef::Gloas(inner) => Ok(inner.execution_payload.blob_gas_used),
}
}
@@ -506,6 +521,7 @@ impl<E: EthSpec> AbstractExecPayload<E> for FullPayload<E> {
type Deneb = FullPayloadDeneb<E>;
type Electra = FullPayloadElectra<E>;
type Fulu = FullPayloadFulu<E>;
type Gloas = FullPayloadGloas<E>;
}
impl<E: EthSpec> From<ExecutionPayload<E>> for FullPayload<E> {
@@ -524,7 +540,7 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for FullPayload<E> {
}
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas),
variant_attributes(
derive(
Debug,
@@ -584,6 +600,8 @@ pub struct BlindedPayload<E: EthSpec> {
pub execution_payload_header: ExecutionPayloadHeaderElectra<E>,
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
pub execution_payload_header: ExecutionPayloadHeaderFulu<E>,
#[superstruct(only(Gloas), partial_getter(rename = "execution_payload_gloas"))]
pub execution_payload_header: ExecutionPayloadHeaderGloas<E>,
}
impl<'a, E: EthSpec> From<BlindedPayloadRef<'a, E>> for BlindedPayload<E> {
@@ -673,6 +691,7 @@ impl<E: EthSpec> ExecPayload<E> for BlindedPayload<E> {
BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Fulu(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Gloas(inner) => Ok(inner.execution_payload_header.withdrawals_root),
}
}
@@ -684,6 +703,7 @@ impl<E: EthSpec> ExecPayload<E> for BlindedPayload<E> {
BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Fulu(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Gloas(inner) => Ok(inner.execution_payload_header.blob_gas_used),
}
}
@@ -783,6 +803,7 @@ impl<'b, E: EthSpec> ExecPayload<E> for BlindedPayloadRef<'b, E> {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayloadRef::Fulu(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayloadRef::Gloas(inner) => Ok(inner.execution_payload_header.withdrawals_root),
}
}
@@ -794,6 +815,7 @@ impl<'b, E: EthSpec> ExecPayload<E> for BlindedPayloadRef<'b, E> {
BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayloadRef::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayloadRef::Fulu(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayloadRef::Gloas(inner) => Ok(inner.execution_payload_header.blob_gas_used),
}
}
@@ -1105,6 +1127,13 @@ impl_exec_payload_for_fork!(
ExecutionPayloadFulu,
Fulu
);
impl_exec_payload_for_fork!(
BlindedPayloadGloas,
FullPayloadGloas,
ExecutionPayloadHeaderGloas,
ExecutionPayloadGloas,
Gloas
);
impl<E: EthSpec> AbstractExecPayload<E> for BlindedPayload<E> {
type Ref<'a> = BlindedPayloadRef<'a, E>;
@@ -1113,6 +1142,7 @@ impl<E: EthSpec> AbstractExecPayload<E> for BlindedPayload<E> {
type Deneb = BlindedPayloadDeneb<E>;
type Electra = BlindedPayloadElectra<E>;
type Fulu = BlindedPayloadFulu<E>;
type Gloas = BlindedPayloadGloas<E>;
}
impl<E: EthSpec> From<ExecutionPayload<E>> for BlindedPayload<E> {
@@ -1154,6 +1184,11 @@ impl<E: EthSpec> From<ExecutionPayloadHeader<E>> for BlindedPayload<E> {
execution_payload_header,
})
}
ExecutionPayloadHeader::Gloas(execution_payload_header) => {
Self::Gloas(BlindedPayloadGloas {
execution_payload_header,
})
}
}
}
}
@@ -1176,6 +1211,9 @@ impl<E: EthSpec> From<BlindedPayload<E>> for ExecutionPayloadHeader<E> {
BlindedPayload::Fulu(blinded_payload) => {
ExecutionPayloadHeader::Fulu(blinded_payload.execution_payload_header)
}
BlindedPayload::Gloas(blinded_payload) => {
ExecutionPayloadHeader::Gloas(blinded_payload.execution_payload_header)
}
}
}
}

View File

@@ -23,7 +23,6 @@ use tree_hash_derive::TreeHash;
use typenum::Unsigned;
use crate::{
BuilderPendingPayment, BuilderPendingWithdrawal, ExecutionBlockHash, ExecutionPayloadBid,
attestation::{
AttestationDuty, BeaconCommittee, Checkpoint, CommitteeIndex, ParticipationFlags,
PendingAttestation,
@@ -35,7 +34,7 @@ use crate::{
execution::{
Eth1Data, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu,
ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
},
fork::{Fork, ForkName, ForkVersionDecode, InconsistentFork, map_fork_name},
light_client::consts::{
@@ -543,9 +542,12 @@ where
)]
#[metastruct(exclude_from(tree_lists))]
pub latest_execution_payload_header: ExecutionPayloadHeaderFulu<E>,
#[superstruct(only(Gloas))]
#[superstruct(
only(Gloas),
partial_getter(rename = "latest_execution_payload_header_gloas")
)]
#[metastruct(exclude_from(tree_lists))]
pub latest_execution_payload_bid: ExecutionPayloadBid,
pub latest_execution_payload_header: ExecutionPayloadHeaderGloas<E>,
#[superstruct(only(Capella, Deneb, Electra, Fulu, Gloas), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
#[metastruct(exclude_from(tree_lists))]
@@ -603,33 +605,6 @@ where
#[serde(with = "ssz_types::serde_utils::quoted_u64_fixed_vec")]
pub proposer_lookahead: Vector<u64, E::ProposerLookaheadSlots>,
// Gloas
#[test_random(default)]
#[superstruct(only(Gloas))]
#[metastruct(exclude_from(tree_lists))]
pub execution_payload_availability: BitVector<E::SlotsPerHistoricalRoot>,
#[compare_fields(as_iter)]
#[test_random(default)]
#[superstruct(only(Gloas))]
pub builder_pending_payments: Vector<BuilderPendingPayment, E::BuilderPendingPaymentsLimit>,
#[compare_fields(as_iter)]
#[test_random(default)]
#[superstruct(only(Gloas))]
pub builder_pending_withdrawals:
List<BuilderPendingWithdrawal, E::BuilderPendingWithdrawalsLimit>,
#[test_random(default)]
#[superstruct(only(Gloas))]
#[metastruct(exclude_from(tree_lists))]
pub latest_block_hash: ExecutionBlockHash,
#[test_random(default)]
#[superstruct(only(Gloas))]
#[metastruct(exclude_from(tree_lists))]
pub latest_withdrawals_root: Hash256,
// Caching (not in the spec)
#[serde(skip_serializing, skip_deserializing)]
#[ssz(skip_serializing, skip_deserializing)]
@@ -1190,8 +1165,9 @@ impl<E: EthSpec> BeaconState<E> {
BeaconState::Fulu(state) => Ok(ExecutionPayloadHeaderRef::Fulu(
&state.latest_execution_payload_header,
)),
// TODO(EIP-7732): investigate calling functions
BeaconState::Gloas(_) => Err(BeaconStateError::IncorrectStateVariant),
BeaconState::Gloas(state) => Ok(ExecutionPayloadHeaderRef::Gloas(
&state.latest_execution_payload_header,
)),
}
}
@@ -1217,8 +1193,9 @@ impl<E: EthSpec> BeaconState<E> {
BeaconState::Fulu(state) => Ok(ExecutionPayloadHeaderRefMut::Fulu(
&mut state.latest_execution_payload_header,
)),
// TODO(EIP-7732): investigate calling functions
BeaconState::Gloas(_) => Err(BeaconStateError::IncorrectStateVariant),
BeaconState::Gloas(state) => Ok(ExecutionPayloadHeaderRefMut::Gloas(
&mut state.latest_execution_payload_header,
)),
}
}
@@ -2295,15 +2272,12 @@ impl<E: EthSpec> BeaconState<E> {
pub fn is_parent_block_full(&self) -> bool {
match self {
BeaconState::Base(_) | BeaconState::Altair(_) => false,
// TODO(EIP-7732): check the implications of this when we get to forkchoice modifications
BeaconState::Bellatrix(_)
| BeaconState::Capella(_)
| BeaconState::Deneb(_)
| BeaconState::Electra(_)
| BeaconState::Fulu(_) => true,
BeaconState::Gloas(state) => {
state.latest_execution_payload_bid.block_hash == state.latest_block_hash
}
| BeaconState::Fulu(_)
| BeaconState::Gloas(_) => true,
}
}

View File

@@ -311,6 +311,7 @@ impl<Engine: GenericExecutionEngine> TestRig<Engine> {
Address::repeat_byte(42),
Some(vec![]),
None,
None,
),
)
.await;
@@ -355,6 +356,7 @@ impl<Engine: GenericExecutionEngine> TestRig<Engine> {
suggested_fee_recipient,
Some(vec![]),
None,
None,
);
let payload_parameters = PayloadParameters {
@@ -513,6 +515,7 @@ impl<Engine: GenericExecutionEngine> TestRig<Engine> {
suggested_fee_recipient,
Some(vec![]),
None,
None,
);
let payload_parameters = PayloadParameters {
@@ -570,9 +573,10 @@ impl<Engine: GenericExecutionEngine> TestRig<Engine> {
let payload_attributes = PayloadAttributes::new(
timestamp,
prev_randao,
Address::repeat_byte(42),
suggested_fee_recipient,
Some(vec![]),
None,
None,
);
let slot = Slot::new(42);
let head_block_root = Hash256::repeat_byte(100);