reth_ethereum_engine_primitives/
payload.rs1use alloc::{sync::Arc, vec::Vec};
4use alloy_eips::{
5 eip4844::BlobTransactionSidecar,
6 eip7594::{BlobTransactionSidecarEip7594, BlobTransactionSidecarVariant},
7 eip7685::Requests,
8};
9use alloy_primitives::{Bytes, U256};
10use alloy_rpc_types_engine::{
11 BlobsBundleV1, BlobsBundleV2, CancunPayloadFields, ExecutionData, ExecutionPayload,
12 ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadEnvelopeV4,
13 ExecutionPayloadEnvelopeV5, ExecutionPayloadEnvelopeV6, ExecutionPayloadFieldV2,
14 ExecutionPayloadSidecar, ExecutionPayloadV1, ExecutionPayloadV3, ExecutionPayloadV4,
15 PraguePayloadFields,
16};
17use reth_ethereum_primitives::EthPrimitives;
18use reth_payload_primitives::BuiltPayload;
19use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SealedBlock};
20
21use crate::BuiltPayloadConversionError;
22
23#[derive(Debug, Clone)]
31pub struct EthBuiltPayload<N: NodePrimitives = EthPrimitives> {
32 pub(crate) block: Arc<RecoveredBlock<N::Block>>,
34 pub(crate) fees: U256,
36 pub(crate) sidecars: BlobSidecars,
39 pub(crate) requests: Option<Requests>,
41 pub(crate) block_access_list: Option<Bytes>,
43}
44
45impl<N: NodePrimitives> EthBuiltPayload<N> {
48 pub const fn new(
52 block: Arc<RecoveredBlock<N::Block>>,
53 fees: U256,
54 requests: Option<Requests>,
55 block_access_list: Option<Bytes>,
56 ) -> Self {
57 Self { block, fees, requests, sidecars: BlobSidecars::Empty, block_access_list }
58 }
59
60 pub fn block(&self) -> &SealedBlock<N::Block> {
62 self.block.sealed_block()
63 }
64
65 pub fn recovered_block(&self) -> &RecoveredBlock<N::Block> {
67 &self.block
68 }
69
70 pub const fn block_arc(&self) -> &Arc<RecoveredBlock<N::Block>> {
72 &self.block
73 }
74
75 pub fn into_block_arc(self) -> Arc<RecoveredBlock<N::Block>> {
77 self.block
78 }
79
80 pub const fn fees(&self) -> U256 {
82 self.fees
83 }
84
85 pub const fn sidecars(&self) -> &BlobSidecars {
87 &self.sidecars
88 }
89
90 pub fn with_sidecars(mut self, sidecars: impl Into<BlobSidecars>) -> Self {
92 self.sidecars = sidecars.into();
93 self
94 }
95}
96
97impl EthBuiltPayload {
98 pub fn try_into_v3(self) -> Result<ExecutionPayloadEnvelopeV3, BuiltPayloadConversionError> {
102 let Self { block, fees, sidecars, .. } = self;
103
104 let blobs_bundle = match sidecars {
105 BlobSidecars::Empty => BlobsBundleV1::empty(),
106 BlobSidecars::Eip4844(sidecars) => BlobsBundleV1::from(sidecars),
107 BlobSidecars::Eip7594(_) => {
108 return Err(BuiltPayloadConversionError::UnexpectedEip7594Sidecars)
109 }
110 };
111
112 Ok(ExecutionPayloadEnvelopeV3 {
113 execution_payload: ExecutionPayloadV3::from_block_unchecked(
114 block.hash(),
115 &Arc::unwrap_or_clone(block).into_block(),
116 ),
117 block_value: fees,
118 should_override_builder: false,
127 blobs_bundle,
128 })
129 }
130
131 pub fn try_into_v4(
135 mut self,
136 ) -> Result<ExecutionPayloadEnvelopeV4, BuiltPayloadConversionError> {
137 let execution_requests = self.requests.take().unwrap_or_default();
138 Ok(ExecutionPayloadEnvelopeV4 { execution_requests, envelope_inner: self.try_into()? })
139 }
140
141 pub fn try_into_v5(self) -> Result<ExecutionPayloadEnvelopeV5, BuiltPayloadConversionError> {
143 let Self { block, fees, sidecars, requests, .. } = self;
144
145 let blobs_bundle = match sidecars {
146 BlobSidecars::Empty => BlobsBundleV2::empty(),
147 BlobSidecars::Eip7594(sidecars) => BlobsBundleV2::from(sidecars),
148 BlobSidecars::Eip4844(_) => {
149 return Err(BuiltPayloadConversionError::UnexpectedEip4844Sidecars)
150 }
151 };
152
153 Ok(ExecutionPayloadEnvelopeV5 {
154 execution_payload: ExecutionPayloadV3::from_block_unchecked(
155 block.hash(),
156 &Arc::unwrap_or_clone(block).into_block(),
157 ),
158 block_value: fees,
159 should_override_builder: false,
168 blobs_bundle,
169 execution_requests: requests.unwrap_or_default(),
170 })
171 }
172
173 pub fn try_into_v6(self) -> Result<ExecutionPayloadEnvelopeV6, BuiltPayloadConversionError> {
177 let Self { block, fees, sidecars, requests, block_access_list, .. } = self;
178
179 let block_access_list =
180 block_access_list.ok_or(BuiltPayloadConversionError::MissingBlockAccessList)?;
181
182 let blobs_bundle = match sidecars {
183 BlobSidecars::Empty => BlobsBundleV2::empty(),
184 BlobSidecars::Eip7594(sidecars) => BlobsBundleV2::from(sidecars),
185 BlobSidecars::Eip4844(_) => {
186 return Err(BuiltPayloadConversionError::UnexpectedEip4844Sidecars)
187 }
188 };
189 Ok(ExecutionPayloadEnvelopeV6 {
190 execution_payload: ExecutionPayloadV4::from_block_unchecked_with_bal(
191 block.hash(),
192 &Arc::unwrap_or_clone(block).into_block(),
193 block_access_list,
194 ),
195 block_value: fees,
196 should_override_builder: false,
205 blobs_bundle,
206 execution_requests: requests.unwrap_or_default(),
207 })
208 }
209
210 pub fn into_execution_data(self) -> ExecutionData {
212 let Self { block, requests, block_access_list, .. } = self;
213 let block_hash = block.hash();
214 let block = Arc::unwrap_or_clone(block).into_block();
215
216 let (payload, sidecar) = ExecutionPayload::from_block_unchecked_with_extras(
217 block_hash,
218 &block,
219 block_access_list,
220 );
221
222 let sidecar = if let Some(requests) = requests {
223 block.header.parent_beacon_block_root.map_or(sidecar, |parent_beacon_block_root| {
224 ExecutionPayloadSidecar::v4(
225 CancunPayloadFields {
226 parent_beacon_block_root,
227 versioned_hashes: block
228 .body
229 .blob_versioned_hashes_iter()
230 .copied()
231 .collect(),
232 },
233 PraguePayloadFields::new(requests),
234 )
235 })
236 } else {
237 sidecar
238 };
239
240 ExecutionData::new(payload, sidecar)
241 }
242}
243
244impl<N: NodePrimitives> BuiltPayload for EthBuiltPayload<N> {
245 type Primitives = N;
246
247 fn block(&self) -> &SealedBlock<N::Block> {
248 self.block.sealed_block()
249 }
250
251 fn fees(&self) -> U256 {
252 self.fees
253 }
254
255 fn block_access_list(&self) -> Option<&Bytes> {
256 self.block_access_list.as_ref()
257 }
258
259 fn requests(&self) -> Option<Requests> {
260 self.requests.clone()
261 }
262}
263
264impl From<EthBuiltPayload> for ExecutionPayloadV1 {
266 fn from(value: EthBuiltPayload) -> Self {
267 Self::from_block_unchecked(
268 value.block().hash(),
269 &Arc::unwrap_or_clone(value.block).into_block(),
270 )
271 }
272}
273
274impl From<EthBuiltPayload> for ExecutionPayloadEnvelopeV2 {
276 fn from(value: EthBuiltPayload) -> Self {
277 let EthBuiltPayload { block, fees, .. } = value;
278
279 Self {
280 block_value: fees,
281 execution_payload: ExecutionPayloadFieldV2::from_block_unchecked(
282 block.hash(),
283 &Arc::unwrap_or_clone(block).into_block(),
284 ),
285 }
286 }
287}
288
289impl TryFrom<EthBuiltPayload> for ExecutionPayloadEnvelopeV3 {
290 type Error = BuiltPayloadConversionError;
291
292 fn try_from(value: EthBuiltPayload) -> Result<Self, Self::Error> {
293 value.try_into_v3()
294 }
295}
296
297impl TryFrom<EthBuiltPayload> for ExecutionPayloadEnvelopeV4 {
298 type Error = BuiltPayloadConversionError;
299
300 fn try_from(value: EthBuiltPayload) -> Result<Self, Self::Error> {
301 value.try_into_v4()
302 }
303}
304
305impl TryFrom<EthBuiltPayload> for ExecutionPayloadEnvelopeV5 {
306 type Error = BuiltPayloadConversionError;
307
308 fn try_from(value: EthBuiltPayload) -> Result<Self, Self::Error> {
309 value.try_into_v5()
310 }
311}
312
313impl TryFrom<EthBuiltPayload> for ExecutionPayloadEnvelopeV6 {
314 type Error = BuiltPayloadConversionError;
315
316 fn try_from(value: EthBuiltPayload) -> Result<Self, Self::Error> {
317 value.try_into_v6()
318 }
319}
320
321impl From<EthBuiltPayload> for ExecutionData {
322 fn from(value: EthBuiltPayload) -> Self {
323 value.into_execution_data()
324 }
325}
326
327#[cfg(feature = "std")]
328impl From<EthBuiltPayload> for reth_engine_primitives::BigBlockData<ExecutionData> {
329 fn from(_value: EthBuiltPayload) -> Self {
330 unreachable!("payload building is not supported for big blocks");
331 }
332}
333
334#[derive(Clone, Default, Debug)]
336pub enum BlobSidecars {
337 #[default]
339 Empty,
340 Eip4844(Vec<BlobTransactionSidecar>),
342 Eip7594(Vec<BlobTransactionSidecarEip7594>),
344}
345
346impl BlobSidecars {
347 pub const fn eip4844(sidecars: Vec<BlobTransactionSidecar>) -> Self {
349 Self::Eip4844(sidecars)
350 }
351
352 pub const fn eip7594(sidecars: Vec<BlobTransactionSidecarEip7594>) -> Self {
354 Self::Eip7594(sidecars)
355 }
356
357 pub fn push_eip4844_sidecar(&mut self, sidecar: BlobTransactionSidecar) {
359 match self {
360 Self::Empty => {
361 *self = Self::Eip4844(Vec::from([sidecar]));
362 }
363 Self::Eip4844(sidecars) => {
364 sidecars.push(sidecar);
365 }
366 Self::Eip7594(_) => {}
367 }
368 }
369
370 pub fn push_eip7594_sidecar(&mut self, sidecar: BlobTransactionSidecarEip7594) {
372 match self {
373 Self::Empty => {
374 *self = Self::Eip7594(Vec::from([sidecar]));
375 }
376 Self::Eip7594(sidecars) => {
377 sidecars.push(sidecar);
378 }
379 Self::Eip4844(_) => {}
380 }
381 }
382
383 pub fn push_sidecar_variant(&mut self, sidecar: BlobTransactionSidecarVariant) {
386 match sidecar {
387 BlobTransactionSidecarVariant::Eip4844(sidecar) => {
388 self.push_eip4844_sidecar(sidecar);
389 }
390 BlobTransactionSidecarVariant::Eip7594(sidecar) => {
391 self.push_eip7594_sidecar(sidecar);
392 }
393 }
394 }
395}
396
397impl From<Vec<BlobTransactionSidecar>> for BlobSidecars {
398 fn from(value: Vec<BlobTransactionSidecar>) -> Self {
399 Self::eip4844(value)
400 }
401}
402
403impl From<Vec<BlobTransactionSidecarEip7594>> for BlobSidecars {
404 fn from(value: Vec<BlobTransactionSidecarEip7594>) -> Self {
405 Self::eip7594(value)
406 }
407}
408
409impl From<alloc::vec::IntoIter<BlobTransactionSidecar>> for BlobSidecars {
410 fn from(value: alloc::vec::IntoIter<BlobTransactionSidecar>) -> Self {
411 value.collect::<Vec<_>>().into()
412 }
413}
414
415impl From<alloc::vec::IntoIter<BlobTransactionSidecarEip7594>> for BlobSidecars {
416 fn from(value: alloc::vec::IntoIter<BlobTransactionSidecarEip7594>) -> Self {
417 value.collect::<Vec<_>>().into()
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424 use alloy_primitives::B256;
425 use reth_primitives_traits::{Block as _, RecoveredBlock};
426
427 #[test]
428 fn into_execution_data_preserves_requests() {
429 let requests = Requests::from_requests([Bytes::from_static(&[1, 2])]);
430 let parent_beacon_block_root = B256::with_last_byte(1);
431
432 let mut block = reth_ethereum_primitives::Block::default();
433 block.header.parent_beacon_block_root = Some(parent_beacon_block_root);
434 block.header.requests_hash = Some(requests.requests_hash());
435
436 let payload = EthBuiltPayload::new(
437 Arc::new(RecoveredBlock::new_sealed(block.seal_slow(), vec![])),
438 U256::ZERO,
439 Some(requests.clone()),
440 None,
441 );
442
443 let execution_data: ExecutionData = payload.into();
444
445 assert_eq!(
446 execution_data.sidecar.parent_beacon_block_root(),
447 Some(parent_beacon_block_root)
448 );
449 assert_eq!(execution_data.sidecar.requests(), Some(&requests));
450 }
451
452 #[test]
453 fn into_execution_data_preserves_block_access_list() {
454 let block_access_list = Bytes::from_static(&[0xc0]);
455
456 let mut block = reth_ethereum_primitives::Block::default();
457 block.header.parent_beacon_block_root = Some(B256::ZERO);
458 block.header.block_access_list_hash = Some(B256::ZERO);
459
460 let payload = EthBuiltPayload::new(
461 Arc::new(RecoveredBlock::new_sealed(block.seal_slow(), vec![])),
462 U256::ZERO,
463 None,
464 Some(block_access_list.clone()),
465 );
466
467 let execution_data: ExecutionData = payload.into();
468
469 assert_eq!(execution_data.payload.block_access_list(), Some(&block_access_list));
470 }
471
472 #[test]
473 fn execution_data_has_infallible_try_from_impl() {
474 fn assert_try_from<T: TryFrom<EthBuiltPayload, Error = core::convert::Infallible>>() {}
475
476 assert_try_from::<ExecutionData>();
477 }
478}