This commit is contained in:
Pawan Dhananjay
2026-01-19 14:51:32 -08:00
parent 47094d592c
commit 658163cfde
16 changed files with 468 additions and 44 deletions

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,12 @@
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_RANGE_V1, 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,
@@ -159,7 +160,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")
@@ -172,10 +173,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 {
@@ -185,24 +188,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,
@@ -247,6 +261,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,
}),
}
}
}
@@ -566,6 +595,7 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
// 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!(
@@ -584,9 +614,11 @@ 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_range_v1: bool,
pub get_payload_v1: bool,
@@ -594,6 +626,7 @@ pub struct EngineCapabilities {
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,
@@ -614,6 +647,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);
}
@@ -623,6 +659,9 @@ 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);
}
@@ -644,6 +683,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,11 +44,13 @@ 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";
@@ -74,14 +78,17 @@ 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_RANGE_V1,
ENGINE_GET_CLIENT_VERSION_V1,
@@ -912,6 +919,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 +1137,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 +1255,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>,
@@ -1198,9 +1380,11 @@ 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_range_v1: capabilities
@@ -1210,6 +1394,7 @@ impl HttpJsonRpc {
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 +1538,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 +1589,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 +1635,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

@@ -110,6 +110,9 @@ pub struct JsonExecutionPayload<E: EthSpec> {
#[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> {
@@ -256,6 +259,7 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadGloas<E>> for JsonExecutionPayloadGloas
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,
})
}
}
@@ -430,6 +434,7 @@ impl<E: EthSpec> TryFrom<JsonExecutionPayloadGloas<E>> for ExecutionPayloadGloas
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,
})
}
}
@@ -721,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")
@@ -737,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 {
@@ -764,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,
}),
}
}
}
@@ -789,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,
}),
}
}
}

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

@@ -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,
@@ -726,6 +729,7 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
blob_gas_used: 0,
excess_blob_gas: 0,
block_access_list: VariableList::empty(),
slot_number: pa.slot_number,
}),
_ => unreachable!(),
},

View File

@@ -922,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,9 +45,11 @@ 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_range_v1: true,
get_payload_v1: true,
@@ -55,6 +57,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
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

@@ -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

@@ -113,6 +113,10 @@ pub struct ExecutionPayload<E: EthSpec> {
#[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

@@ -117,6 +117,10 @@ pub struct ExecutionPayloadHeader<E: EthSpec> {
/// 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> {
@@ -291,6 +295,7 @@ impl<E: EthSpec> ExecutionPayloadHeaderFulu<E> {
blob_gas_used: self.blob_gas_used,
excess_blob_gas: self.excess_blob_gas,
block_access_list_root: Hash256::zero(),
slot_number: 0,
}
}
}
@@ -431,6 +436,7 @@ impl<'a, E: EthSpec> From<&'a ExecutionPayloadGloas<E>> for ExecutionPayloadHead
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,
}
}
}

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);