diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index 52a58c88fec..3d996c7df2d 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -3,10 +3,10 @@ use crate::http::{ ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3, ENGINE_FORKCHOICE_UPDATED_V4, ENGINE_GET_BLOBS_V2, ENGINE_GET_CLIENT_VERSION_V1, ENGINE_GET_INCLUSION_LIST_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, + ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2, 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, @@ -25,9 +25,9 @@ pub use types::{ Withdrawal, Withdrawals, }; use types::{ - ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, + BlockAccessList, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionPayloadHeze, - ExecutionRequests, KzgProofs, + ExecutionRequests, KzgProofs, ProgressiveTransactions, ProgressiveWithdrawals, }; use types::{GRAFFITI_BYTES_LEN, Graffiti}; @@ -449,6 +449,14 @@ pub struct ExecutionPayloadBodyV1 { pub withdrawals: Option>, } +/// The execution payload body returned by `engine_getPayloadBodiesByHashV2`. +#[derive(Clone, Debug, PartialEq)] +pub struct ExecutionPayloadBodyV2 { + pub transactions: ProgressiveTransactions, + pub withdrawals: ProgressiveWithdrawals, + pub block_access_list: BlockAccessList, +} + impl ExecutionPayloadBodyV1 { pub fn to_payload( self, @@ -605,6 +613,7 @@ pub struct EngineCapabilities { 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_v1: bool, pub get_payload_v2: bool, @@ -651,6 +660,9 @@ impl EngineCapabilities { 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); } diff --git a/beacon_node/execution_layer/src/engine_api/http.rs b/beacon_node/execution_layer/src/engine_api/http.rs index b6e755471e0..e5bcf0ee29d 100644 --- a/beacon_node/execution_layer/src/engine_api/http.rs +++ b/beacon_node/execution_layer/src/engine_api/http.rs @@ -55,6 +55,7 @@ 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_TIMEOUT: Duration = Duration::from_secs(10); @@ -94,6 +95,7 @@ pub static LIGHTHOUSE_CAPABILITIES: &[&str] = &[ 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_CLIENT_VERSION_V1, ENGINE_GET_BLOBS_V2, @@ -1204,6 +1206,26 @@ impl HttpJsonRpc { .collect::, _>>() } + pub async fn get_payload_bodies_by_hash_v2( + &self, + block_hashes: Vec, + ) -> Result>, Error> { + let params = json!([block_hashes]); + + let response: Vec> = self + .rpc_request( + ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2, + params, + ENGINE_GET_PAYLOAD_BODIES_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + + Ok(response + .into_iter() + .map(|body| body.map(Into::into)) + .collect()) + } + pub async fn get_payload_bodies_by_range_v1( &self, start: u64, @@ -1255,6 +1277,8 @@ impl HttpJsonRpc { 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_v1: capabilities.contains(ENGINE_GET_PAYLOAD_V1), diff --git a/beacon_node/execution_layer/src/engine_api/json_structures.rs b/beacon_node/execution_layer/src/engine_api/json_structures.rs index b6f5f820034..2f18232d6ad 100644 --- a/beacon_node/execution_layer/src/engine_api/json_structures.rs +++ b/beacon_node/execution_layer/src/engine_api/json_structures.rs @@ -1215,6 +1215,36 @@ pub struct JsonExecutionPayloadBodyV1 { pub withdrawals: Option>, } +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct JsonExecutionPayloadBodyV2 { + #[serde(with = "ssz_types::serde_utils::prog_list_of_hex_prog_var_list")] + pub transactions: ProgressiveTransactions, + pub withdrawals: ProgressiveVariableList, + #[serde(with = "ssz_types::serde_utils::hex_prog_var_list")] + pub block_access_list: BlockAccessList, +} + +impl From for ExecutionPayloadBodyV2 { + fn from(value: JsonExecutionPayloadBodyV2) -> Self { + Self { + transactions: value.transactions, + withdrawals: value.withdrawals.into_iter().map(Into::into).collect(), + block_access_list: value.block_access_list, + } + } +} + +impl From for JsonExecutionPayloadBodyV2 { + fn from(value: ExecutionPayloadBodyV2) -> Self { + Self { + transactions: value.transactions, + withdrawals: value.withdrawals.into_iter().map(Into::into).collect(), + block_access_list: value.block_access_list, + } + } +} + impl TryFrom> for ExecutionPayloadBodyV1 { type Error = ssz_types::Error; diff --git a/beacon_node/execution_layer/src/lib.rs b/beacon_node/execution_layer/src/lib.rs index 29236ca7687..d78bd9f2e4c 100644 --- a/beacon_node/execution_layer/src/lib.rs +++ b/beacon_node/execution_layer/src/lib.rs @@ -1674,6 +1674,20 @@ impl ExecutionLayer { .map_err(Error::EngineError) } + /// Fetch Gloas execution payload bodies by block hash. + pub async fn get_payload_bodies_by_hash_v2( + &self, + hashes: Vec, + ) -> Result>, Error> { + self.engine() + .request(|engine: &Engine| async move { + engine.api.get_payload_bodies_by_hash_v2(hashes).await + }) + .await + .map_err(Box::new) + .map_err(Error::EngineError) + } + pub async fn get_payload_bodies_by_range( &self, start: u64, @@ -2175,7 +2189,7 @@ fn noop( #[cfg(test)] mod test { use super::*; - use crate::test_utils::MockExecutionLayer as GenericMockExecutionLayer; + use crate::test_utils::{Block, MockExecutionLayer as GenericMockExecutionLayer}; use task_executor::test_utils::TestRuntime; use types::MainnetEthSpec; @@ -2193,6 +2207,33 @@ mod test { .await; } + #[tokio::test] + async fn get_gloas_payload_bodies_by_hash() { + let runtime = TestRuntime::default(); + let mock = MockExecutionLayer::default_params(runtime.task_executor.clone()); + let block_hash = ExecutionBlockHash::repeat_byte(0x42); + let payload = ExecutionPayloadGloas { + block_hash, + ..Default::default() + }; + let expected_body = ExecutionPayloadBodyV2 { + transactions: payload.transactions.clone(), + withdrawals: payload.withdrawals.clone(), + block_access_list: payload.block_access_list.clone(), + }; + mock.server + .execution_block_generator() + .insert_block_without_checks(Block::PoS(payload.clone().into())); + + let bodies = mock + .el + .get_payload_bodies_by_hash_v2(vec![block_hash, ExecutionBlockHash::zero()]) + .await + .expect("payload body request should succeed"); + + assert_eq!(bodies, vec![Some(expected_body), None]); + } + #[tokio::test] async fn test_expected_gas_limit() { let spec = ChainSpec::mainnet(); diff --git a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs index c1e72a6f3ed..a048f157925 100644 --- a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs +++ b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs @@ -768,6 +768,33 @@ pub async fn handle_rpc( Ok(serde_json::to_value(response).unwrap()) } + ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2 => { + let block_hashes = get_param::>(params, 0) + .map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?; + + let mut response = vec![]; + for block_hash in block_hashes { + let payload = ctx + .execution_block_generator + .read() + .execution_payload_by_hash(block_hash); + + match payload { + Some(ExecutionPayload::Gloas(payload)) => { + response.push(Some(JsonExecutionPayloadBodyV2::from( + ExecutionPayloadBodyV2 { + transactions: payload.transactions, + withdrawals: payload.withdrawals, + block_access_list: payload.block_access_list, + }, + ))); + } + _ => response.push(None), + } + } + + Ok(serde_json::to_value(response).unwrap()) + } ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1 => { #[derive(Deserialize)] #[serde(transparent)] diff --git a/beacon_node/execution_layer/src/test_utils/mod.rs b/beacon_node/execution_layer/src/test_utils/mod.rs index 82b55551367..2bdc0d11c07 100644 --- a/beacon_node/execution_layer/src/test_utils/mod.rs +++ b/beacon_node/execution_layer/src/test_utils/mod.rs @@ -49,6 +49,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities { 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_v1: true, get_payload_v2: true,