diff --git a/Cargo.toml b/Cargo.toml index 067b0b24..7c4d63ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,17 +45,17 @@ op-alloy-rpc-types-engine = { version = "0.18.9", path = "crates/rpc-types-engin op-alloy-rpc-jsonrpsee = { version = "0.18.9", path = "crates/rpc-jsonrpsee", default-features = false } # Alloy -alloy-eips = { version = "1.0.15", default-features = false } -alloy-serde = { version = "1.0.15", default-features = false } -alloy-signer = { version = "1.0.15", default-features = false } -alloy-network = { version = "1.0.15", default-features = false } -alloy-provider = { version = "1.0.15", default-features = false } -alloy-transport = { version = "1.0.15", default-features = false } -alloy-consensus = { version = "1.0.15", default-features = false } -alloy-rpc-types-eth = { version = "1.0.15", default-features = false } -alloy-rpc-types-engine = { version = "1.0.15", default-features = false } -alloy-network-primitives = { version = "1.0.15", default-features = false } -alloy-json-rpc = { version = "1.0.15", default-features = false } +alloy-eips = { version = "1.0.22", default-features = false } +alloy-serde = { version = "1.0.22", default-features = false } +alloy-signer = { version = "1.0.22", default-features = false } +alloy-network = { version = "1.0.22", default-features = false } +alloy-provider = { version = "1.0.22", default-features = false } +alloy-transport = { version = "1.0.22", default-features = false } +alloy-consensus = { version = "1.0.22", default-features = false } +alloy-rpc-types-eth = { version = "1.0.22", default-features = false } +alloy-rpc-types-engine = { version = "1.0.22", default-features = false } +alloy-network-primitives = { version = "1.0.22", default-features = false } +alloy-json-rpc = { version = "1.0.22", default-features = false } # Alloy RLP alloy-rlp = { version = "0.3", default-features = false } diff --git a/crates/rpc-types-engine/src/payload/mod.rs b/crates/rpc-types-engine/src/payload/mod.rs index 78c348e0..0d549143 100644 --- a/crates/rpc-types-engine/src/payload/mod.rs +++ b/crates/rpc-types-engine/src/payload/mod.rs @@ -460,6 +460,57 @@ impl OpExecutionPayload { self.as_v1().timestamp } + /// Converts [`OpExecutionPayload`] to [`Block`] with raw transactions. + /// + /// Caution: This does not set fields that are not part of the payload and only part of the + /// [`OpExecutionPayloadSidecar`]: + /// - parent_beacon_block_root + /// + /// See also: [`OpExecutionPayload::into_block_with_sidecar_raw`] + pub fn into_block_raw(self) -> Result, PayloadError> { + match self { + Self::V1(payload) => payload.into_block_raw(), + Self::V2(payload) => payload.into_block_raw(), + Self::V3(payload) => payload.into_block_raw(), + Self::V4(payload) => payload.into_block_raw(), + } + } + + /// Creates a new unsealed block from the given payload and payload sidecar with raw + /// transactions. + /// + /// This sets the `parent_beacon_block_root` and `requests_hash` if present in the sidecar. + /// Also validates that L1 withdrawals are empty. + /// + /// See also: [`OpExecutionPayload::try_into_block_with_sidecar`] + pub fn into_block_with_sidecar_raw( + self, + sidecar: &OpExecutionPayloadSidecar, + ) -> Result, OpPayloadError> { + if let Some(payload) = self.as_v2() { + if !payload.withdrawals.is_empty() { + return Err(OpPayloadError::NonEmptyL1Withdrawals); + } + } + + let mut block = self.into_block_raw()?; + + if let Some(blobs_hashes) = sidecar.versioned_hashes() { + if !blobs_hashes.is_empty() { + return Err(OpPayloadError::NonEmptyBlobVersionedHashes); + } + } + if let Some(reqs_hash) = sidecar.requests_hash() { + if reqs_hash != EMPTY_REQUESTS_HASH { + return Err(OpPayloadError::NonEmptyELRequests); + } + block.header.requests_hash = Some(EMPTY_REQUESTS_HASH) + } + block.header.parent_beacon_block_root = sidecar.parent_beacon_block_root(); + + Ok(block) + } + #[allow(rustdoc::broken_intra_doc_links)] /// Converts [`OpExecutionPayload`] to [`Block`]. /// diff --git a/crates/rpc-types-engine/src/payload/v4.rs b/crates/rpc-types-engine/src/payload/v4.rs index 3a32fb4b..10524ecc 100644 --- a/crates/rpc-types-engine/src/payload/v4.rs +++ b/crates/rpc-types-engine/src/payload/v4.rs @@ -35,6 +35,19 @@ impl OpExecutionPayloadV4 { Self { withdrawals_root, payload_inner: payload } } + /// Converts [`OpExecutionPayloadV4`] to [`Block`] with raw transactions. + /// + /// This performs the same conversion as the underlying V3 payload, but inserts the L2 + /// withdrawals root and returns raw transaction bytes instead of decoded transactions. + pub fn into_block_raw(self) -> Result, PayloadError> { + let mut base_block = self.payload_inner.into_block_raw()?; + + // overwrite l1 withdrawals root with l2 withdrawals root + base_block.header.withdrawals_root = Some(self.withdrawals_root); + + Ok(base_block) + } + /// Converts [`OpExecutionPayloadV4`] to [`Block`]. /// /// This performs the same conversion as the underlying V3 payload, but inserts the L2 @@ -42,7 +55,8 @@ impl OpExecutionPayloadV4 { /// /// See also [`ExecutionPayloadV3::try_into_block`]. pub fn try_into_block(self) -> Result, PayloadError> { - self.try_into_block_with(|tx| { + let block = self.into_block_raw()?; + block.try_map_transactions(|tx| { T::decode_2718_exact(tx.as_ref()) .map_err(alloy_rlp::Error::from) .map_err(PayloadError::from) @@ -60,12 +74,8 @@ impl OpExecutionPayloadV4 { F: FnMut(Bytes) -> Result, E: Into, { - let mut base_block = self.payload_inner.try_into_block_with(f)?; - - // overwrite l1 withdrawals root with l2 withdrawals root - base_block.header.withdrawals_root = Some(self.withdrawals_root); - - Ok(base_block) + let block = self.into_block_raw()?; + block.try_map_transactions(f).map_err(|e| e.into()) } }