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