diff --git a/crates/rpc-types-engine/src/payload.rs b/crates/rpc-types-engine/src/payload.rs index bc963f7a797..fe8eb7f29a4 100644 --- a/crates/rpc-types-engine/src/payload.rs +++ b/crates/rpc-types-engine/src/payload.rs @@ -309,6 +309,19 @@ impl ExecutionPayloadV1 { /// Converts [`ExecutionPayloadV1`] to [`Block`] pub fn try_into_block(self) -> Result, PayloadError> { + self.try_into_block_with(|tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + /// Converts [`ExecutionPayloadV1`] to [`Block`] with the given closure. + pub fn try_into_block_with(self, f: F) -> Result, PayloadError> + where + F: FnMut(Bytes) -> Result, + E: Into, + { if self.extra_data.len() > MAXIMUM_EXTRA_DATA_SIZE { return Err(PayloadError::ExtraData(self.extra_data)); } @@ -317,28 +330,20 @@ impl ExecutionPayloadV1 { return Err(PayloadError::BaseFee(self.base_fee_per_gas)); } - let transactions = self - .transactions - .iter() - .map(|tx| { - let mut buf = tx.as_ref(); - - let tx = T::decode_2718(&mut buf).map_err(alloy_rlp::Error::from)?; - - if !buf.is_empty() { - return Err(alloy_rlp::Error::UnexpectedLength); - } - - Ok(tx) - }) - .collect::, _>>()?; - - // Reuse the encoded bytes for root calculation + // Calculate the transactions root using encoded bytes let transactions_root = alloy_consensus::proofs::ordered_trie_root_with_encoder( &self.transactions, |item, buf| buf.put_slice(item), ); + // Convert transactions using the provided mapper + let transactions = self + .transactions + .into_iter() + .map(f) + .collect::, E>>() + .map_err(Into::into)?; + let header = Header { parent_hash: self.parent_hash, beneficiary: self.fee_recipient, @@ -352,7 +357,7 @@ impl ExecutionPayloadV1 { gas_used: self.gas_used, timestamp: self.timestamp, mix_hash: self.prev_randao, - // WARNING: It’s allowed for a base fee in EIP1559 to increase unbounded. We assume that + // WARNING: It's allowed for a base fee in EIP1559 to increase unbounded. We assume that // it will fit in an u64. This is not always necessarily true, although it is extremely // unlikely not to be the case, a u64 maximum would have 2^64 which equates to 18 ETH // per gas. @@ -501,7 +506,22 @@ impl ExecutionPayloadV2 { /// /// See also [`ExecutionPayloadV1::try_into_block`]. pub fn try_into_block(self) -> Result, PayloadError> { - let mut base_sealed_block = self.payload_inner.try_into_block()?; + self.try_into_block_with(|tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + /// Converts [`ExecutionPayloadV2`] to [`Block`] with a custom transaction mapper. + /// + /// See also [`ExecutionPayloadV1::try_into_block_with`]. + pub fn try_into_block_with(self, f: F) -> Result, PayloadError> + where + F: FnMut(Bytes) -> Result, + E: Into, + { + let mut base_sealed_block = self.payload_inner.try_into_block_with(f)?; let withdrawals_root = alloy_consensus::proofs::calculate_withdrawals_root(&self.withdrawals); base_sealed_block.body.withdrawals = Some(self.withdrawals.into()); @@ -677,7 +697,22 @@ impl ExecutionPayloadV3 { /// /// See also [`ExecutionPayloadV2::try_into_block`]. pub fn try_into_block(self) -> Result, PayloadError> { - let mut base_block = self.payload_inner.try_into_block()?; + self.try_into_block_with(|tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + /// Converts [`ExecutionPayloadV3`] to [`Block`] with a custom transaction mapper. + /// + /// See also [`ExecutionPayloadV2::try_into_block_with`]. + pub fn try_into_block_with(self, f: F) -> Result, PayloadError> + where + F: FnMut(Bytes) -> Result, + E: Into, + { + let mut base_block = self.payload_inner.try_into_block_with(f)?; base_block.header.blob_gas_used = Some(self.blob_gas_used); base_block.header.excess_blob_gas = Some(self.excess_blob_gas); @@ -1015,7 +1050,28 @@ impl ExecutionPayload { self, sidecar: &ExecutionPayloadSidecar, ) -> Result, PayloadError> { - let mut base_payload = self.try_into_block()?; + self.try_into_block_with_sidecar_with(sidecar, |tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + /// Converts [`ExecutionPayload`] to [`Block`] with sidecar and a custom transaction mapper. + /// + /// The log bloom is assumed to be validated during serialization. + /// + /// See + pub fn try_into_block_with_sidecar_with( + self, + sidecar: &ExecutionPayloadSidecar, + f: F, + ) -> Result, PayloadError> + where + F: FnMut(Bytes) -> Result, + E: Into, + { + let mut base_payload = self.try_into_block_with(f)?; base_payload.header.parent_beacon_block_root = sidecar.parent_beacon_block_root(); base_payload.header.requests_hash = sidecar.requests_hash(); @@ -1031,10 +1087,30 @@ impl ExecutionPayload { /// /// See also: [`ExecutionPayload::try_into_block_with_sidecar`] pub fn try_into_block(self) -> Result, PayloadError> { + self.try_into_block_with(|tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + /// Converts [`ExecutionPayload`] to [`Block`] with a custom transaction mapper. + /// + /// Caution: This does not set fields that are not part of the payload and only part of the + /// [`ExecutionPayloadSidecar`]: + /// - parent_beacon_block_root + /// - requests_hash + /// + /// See also: [`ExecutionPayload::try_into_block_with_sidecar`] + pub fn try_into_block_with(self, f: F) -> Result, PayloadError> + where + F: FnMut(Bytes) -> Result, + E: Into, + { match self { - Self::V1(payload) => payload.try_into_block(), - Self::V2(payload) => payload.try_into_block(), - Self::V3(payload) => payload.try_into_block(), + Self::V1(payload) => payload.try_into_block_with(f), + Self::V2(payload) => payload.try_into_block_with(f), + Self::V3(payload) => payload.try_into_block_with(f), } } @@ -1677,7 +1753,32 @@ impl ExecutionData { pub fn try_into_block( self, ) -> Result, PayloadError> { - self.payload.try_into_block_with_sidecar(&self.sidecar) + self.try_into_block_with(|tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + /// Tries to create a new unsealed block from the given payload and payload sidecar with a + /// custom transaction mapper. + /// + /// Performs additional validation of `extra_data` and `base_fee_per_gas` fields. + /// + /// # Note + /// + /// The log bloom is assumed to be validated during serialization. + /// + /// See + pub fn try_into_block_with( + self, + f: F, + ) -> Result, PayloadError> + where + F: FnMut(Bytes) -> Result, + E: Into, + { + self.payload.try_into_block_with_sidecar_with(&self.sidecar, f) } }