Skip to content

Commit

Permalink
Add conversions from block to payload (#460)
Browse files Browse the repository at this point in the history
Adds conversion from block to `OpExecutionPayload` and `OpExecutionData`
  • Loading branch information
emhane authored Feb 27, 2025
1 parent 79eb45f commit bc76cae
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 4 deletions.
33 changes: 32 additions & 1 deletion crates/rpc-types-engine/src/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<T, H>(block: &Block<T, H>) -> 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<T, H>(block_hash: B256, block: &Block<T, H>) -> 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: <https://specs.optimism.io/protocol/exec-engine.html#engine_newpayloadv2>
Expand Down
60 changes: 57 additions & 3 deletions crates/rpc-types-engine/src/payload/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<T, H>(block: &Block<T, H>) -> (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<T, H>(
block_hash: B256,
block: &Block<T, H>,
) -> (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.
///
Expand Down
11 changes: 11 additions & 0 deletions crates/rpc-types-engine/src/payload/v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit bc76cae

Please sign in to comment.