From bc76caedf32fa319fc1dda5f86c9b6da12fbde59 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 27 Feb 2025 16:46:19 +0100 Subject: [PATCH] Add conversions from block to payload (#460) Adds conversion from block to `OpExecutionPayload` and `OpExecutionData` --- crates/rpc-types-engine/src/envelope.rs | 33 +++++++++++- crates/rpc-types-engine/src/payload/mod.rs | 60 ++++++++++++++++++++-- crates/rpc-types-engine/src/payload/v4.rs | 11 ++++ 3 files changed, 100 insertions(+), 4 deletions(-) diff --git a/crates/rpc-types-engine/src/envelope.rs b/crates/rpc-types-engine/src/envelope.rs index 098b11ed..ce1d95b9 100644 --- a/crates/rpc-types-engine/src/envelope.rs +++ b/crates/rpc-types-engine/src/envelope.rs @@ -5,7 +5,8 @@ use crate::{OpExecutionPayload, OpExecutionPayloadSidecar, OpExecutionPayloadV4}; use alloc::vec::Vec; -use alloy_eips::{eip4895::Withdrawal, eip7685::Requests}; +use alloy_consensus::{Block, BlockHeader, Sealable, Transaction}; +use alloy_eips::{eip4895::Withdrawal, eip7685::Requests, Encodable2718}; use alloy_primitives::{keccak256, PrimitiveSignature as Signature, B256}; use alloy_rpc_types_engine::{ CancunPayloadFields, ExecutionPayload, ExecutionPayloadInputV2, ExecutionPayloadV3, @@ -29,6 +30,36 @@ impl OpExecutionData { Self { payload, sidecar } } + /// Conversion from [`alloy_consensus::Block`]. Also returns the [`OpExecutionPayloadSidecar`] + /// extracted from the block. + /// + /// See also [`from_block_unchecked`](OpExecutionPayload::from_block_slow). + /// + /// Note: This re-calculates the block hash. + pub fn from_block_slow(block: &Block) -> Self + where + T: Encodable2718 + Transaction, + H: BlockHeader + Sealable, + { + let (payload, sidecar) = OpExecutionPayload::from_block_slow(block); + + Self::new(payload, sidecar) + } + + /// Conversion from [`alloy_consensus::Block`]. Also returns the [`OpExecutionPayloadSidecar`] + /// extracted from the block. + /// + /// See also [`OpExecutionPayload::from_block_unchecked`]. + pub fn from_block_unchecked(block_hash: B256, block: &Block) -> Self + where + T: Encodable2718 + Transaction, + H: BlockHeader, + { + let (payload, sidecar) = OpExecutionPayload::from_block_unchecked(block_hash, block); + + Self::new(payload, sidecar) + } + /// Creates a new instance from args to engine API method `newPayloadV2`. /// /// Spec: diff --git a/crates/rpc-types-engine/src/payload/mod.rs b/crates/rpc-types-engine/src/payload/mod.rs index 6cca9b6e..2705280d 100644 --- a/crates/rpc-types-engine/src/payload/mod.rs +++ b/crates/rpc-types-engine/src/payload/mod.rs @@ -5,9 +5,9 @@ pub mod v3; pub mod v4; use crate::{OpExecutionPayloadSidecar, OpExecutionPayloadV4}; -use alloy_consensus::{Block, EMPTY_ROOT_HASH}; -use alloy_eips::{Decodable2718, Typed2718}; -use alloy_primitives::B256; +use alloy_consensus::{Block, BlockHeader, Transaction, EMPTY_ROOT_HASH}; +use alloy_eips::{Decodable2718, Encodable2718, Typed2718}; +use alloy_primitives::{Sealable, B256}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, @@ -291,6 +291,60 @@ impl<'de> serde::Deserialize<'de> for OpExecutionPayload { } impl OpExecutionPayload { + /// Conversion from [`alloy_consensus::Block`]. Also returns the + /// [`OpExecutionPayloadSidecar`] extracted from the block. + /// + /// See also [`from_block_unchecked`](OpExecutionPayload::from_block_unchecked). + /// + /// Note: This re-calculates the block hash. + pub fn from_block_slow(block: &Block) -> (Self, OpExecutionPayloadSidecar) + where + T: Encodable2718 + Transaction, + H: BlockHeader + Sealable, + { + Self::from_block_unchecked(block.hash_slow(), block) + } + + /// Conversion from [`alloy_consensus::Block`]. Also returns the + /// [`OpExecutionPayloadSidecar`] extracted from the block. + /// + /// See also [`ExecutionPayload::from_block_unchecked`]. + /// See also [`OpExecutionPayloadSidecar::from_block`]. + pub fn from_block_unchecked( + block_hash: B256, + block: &Block, + ) -> (Self, OpExecutionPayloadSidecar) + where + T: Encodable2718 + Transaction, + H: BlockHeader, + { + let sidecar = OpExecutionPayloadSidecar::from_block(block); + + let execution_payload = match block.withdrawals_root() { + Some(withdrawals_root) if sidecar.isthmus().is_some() => { + // block with (empty) request hashes: V4 + Self::V4(OpExecutionPayloadV4::from_v3_with_withdrawals_root( + ExecutionPayloadV3::from_block_unchecked(block_hash, block), + withdrawals_root, + )) + } + Some(_) if block.header.parent_beacon_block_root().is_some() => { + // block with parent beacon block root: at least V3 + Self::V3(ExecutionPayloadV3::from_block_unchecked(block_hash, block)) + } + Some(_) => { + // block with withdrawals root: at least V2 + Self::V2(ExecutionPayloadV2::from_block_unchecked(block_hash, block)) + } + None => { + // otherwise V1 + Self::V1(ExecutionPayloadV1::from_block_unchecked(block_hash, block)) + } + }; + + (execution_payload, sidecar) + } + /// Creates a new instance from `newPayloadV2` payload, i.e. [`V1`](Self::V1) or /// [`V2`](Self::V2) variant. /// diff --git a/crates/rpc-types-engine/src/payload/v4.rs b/crates/rpc-types-engine/src/payload/v4.rs index f1713831..5b4a1739 100644 --- a/crates/rpc-types-engine/src/payload/v4.rs +++ b/crates/rpc-types-engine/src/payload/v4.rs @@ -22,6 +22,17 @@ pub struct OpExecutionPayloadV4 { } impl OpExecutionPayloadV4 { + /// Converts [`ExecutionPayloadV3`] to [`OpExecutionPayloadV4`] using the given L2 withdrawals + /// root. + /// + /// See also [`ExecutionPayloadV3::from_block_unchecked`]. + pub const fn from_v3_with_withdrawals_root( + payload: ExecutionPayloadV3, + withdrawals_root: B256, + ) -> Self { + Self { withdrawals_root, payload_inner: payload } + } + /// Converts [`OpExecutionPayloadV4`] to [`Block`]. /// /// This performs the same conversion as the underlying V3 payload, but inserts the L2