reth_ethereum_engine_primitives/
payload.rs
1use alloc::{sync::Arc, vec::Vec};
4use alloy_eips::{eip4844::BlobTransactionSidecar, eip4895::Withdrawals, eip7685::Requests};
5use alloy_primitives::{Address, B256, U256};
6use alloy_rlp::Encodable;
7use alloy_rpc_types_engine::{
8 ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadEnvelopeV4,
9 ExecutionPayloadFieldV2, ExecutionPayloadV1, ExecutionPayloadV3, PayloadAttributes, PayloadId,
10};
11use core::convert::Infallible;
12use reth_ethereum_primitives::{Block, EthPrimitives};
13use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes};
14use reth_primitives_traits::SealedBlock;
15
16#[derive(Debug, Clone)]
22pub struct EthBuiltPayload {
23 pub(crate) id: PayloadId,
25 pub(crate) block: Arc<SealedBlock<Block>>,
27 pub(crate) fees: U256,
29 pub(crate) sidecars: Vec<BlobTransactionSidecar>,
32 pub(crate) requests: Option<Requests>,
34}
35
36impl EthBuiltPayload {
39 pub const fn new(
43 id: PayloadId,
44 block: Arc<SealedBlock<Block>>,
45 fees: U256,
46 requests: Option<Requests>,
47 ) -> Self {
48 Self { id, block, fees, sidecars: Vec::new(), requests }
49 }
50
51 pub const fn id(&self) -> PayloadId {
53 self.id
54 }
55
56 pub fn block(&self) -> &SealedBlock<Block> {
58 &self.block
59 }
60
61 pub const fn fees(&self) -> U256 {
63 self.fees
64 }
65
66 pub fn sidecars(&self) -> &[BlobTransactionSidecar] {
68 &self.sidecars
69 }
70
71 pub fn extend_sidecars(&mut self, sidecars: impl IntoIterator<Item = BlobTransactionSidecar>) {
73 self.sidecars.extend(sidecars)
74 }
75
76 pub fn with_sidecars(
78 mut self,
79 sidecars: impl IntoIterator<Item = BlobTransactionSidecar>,
80 ) -> Self {
81 self.extend_sidecars(sidecars);
82 self
83 }
84}
85
86impl BuiltPayload for EthBuiltPayload {
87 type Primitives = EthPrimitives;
88
89 fn block(&self) -> &SealedBlock<Block> {
90 &self.block
91 }
92
93 fn fees(&self) -> U256 {
94 self.fees
95 }
96
97 fn requests(&self) -> Option<Requests> {
98 self.requests.clone()
99 }
100}
101
102impl From<EthBuiltPayload> for ExecutionPayloadV1 {
104 fn from(value: EthBuiltPayload) -> Self {
105 Self::from_block_unchecked(
106 value.block().hash(),
107 &Arc::unwrap_or_clone(value.block).into_block(),
108 )
109 }
110}
111
112impl From<EthBuiltPayload> for ExecutionPayloadEnvelopeV2 {
114 fn from(value: EthBuiltPayload) -> Self {
115 let EthBuiltPayload { block, fees, .. } = value;
116
117 Self {
118 block_value: fees,
119 execution_payload: ExecutionPayloadFieldV2::from_block_unchecked(
120 block.hash(),
121 &Arc::unwrap_or_clone(block).into_block(),
122 ),
123 }
124 }
125}
126
127impl From<EthBuiltPayload> for ExecutionPayloadEnvelopeV3 {
128 fn from(value: EthBuiltPayload) -> Self {
129 let EthBuiltPayload { block, fees, sidecars, .. } = value;
130
131 Self {
132 execution_payload: ExecutionPayloadV3::from_block_unchecked(
133 block.hash(),
134 &Arc::unwrap_or_clone(block).into_block(),
135 ),
136 block_value: fees,
137 should_override_builder: false,
146 blobs_bundle: sidecars.into(),
147 }
148 }
149}
150
151impl From<EthBuiltPayload> for ExecutionPayloadEnvelopeV4 {
152 fn from(value: EthBuiltPayload) -> Self {
153 Self {
154 execution_requests: value.requests.clone().unwrap_or_default(),
155 envelope_inner: value.into(),
156 }
157 }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, Default)]
162pub struct EthPayloadBuilderAttributes {
163 pub id: PayloadId,
165 pub parent: B256,
167 pub timestamp: u64,
171 pub suggested_fee_recipient: Address,
173 pub prev_randao: B256,
175 pub withdrawals: Withdrawals,
177 pub parent_beacon_block_root: Option<B256>,
179}
180
181impl EthPayloadBuilderAttributes {
184 pub const fn payload_id(&self) -> PayloadId {
186 self.id
187 }
188
189 pub fn new(parent: B256, attributes: PayloadAttributes) -> Self {
193 let id = payload_id(&parent, &attributes);
194
195 Self {
196 id,
197 parent,
198 timestamp: attributes.timestamp,
199 suggested_fee_recipient: attributes.suggested_fee_recipient,
200 prev_randao: attributes.prev_randao,
201 withdrawals: attributes.withdrawals.unwrap_or_default().into(),
202 parent_beacon_block_root: attributes.parent_beacon_block_root,
203 }
204 }
205}
206
207impl PayloadBuilderAttributes for EthPayloadBuilderAttributes {
208 type RpcPayloadAttributes = PayloadAttributes;
209 type Error = Infallible;
210
211 fn try_new(
215 parent: B256,
216 attributes: PayloadAttributes,
217 _version: u8,
218 ) -> Result<Self, Infallible> {
219 Ok(Self::new(parent, attributes))
220 }
221
222 fn payload_id(&self) -> PayloadId {
223 self.id
224 }
225
226 fn parent(&self) -> B256 {
227 self.parent
228 }
229
230 fn timestamp(&self) -> u64 {
231 self.timestamp
232 }
233
234 fn parent_beacon_block_root(&self) -> Option<B256> {
235 self.parent_beacon_block_root
236 }
237
238 fn suggested_fee_recipient(&self) -> Address {
239 self.suggested_fee_recipient
240 }
241
242 fn prev_randao(&self) -> B256 {
243 self.prev_randao
244 }
245
246 fn withdrawals(&self) -> &Withdrawals {
247 &self.withdrawals
248 }
249}
250
251pub(crate) fn payload_id(parent: &B256, attributes: &PayloadAttributes) -> PayloadId {
255 use sha2::Digest;
256 let mut hasher = sha2::Sha256::new();
257 hasher.update(parent.as_slice());
258 hasher.update(&attributes.timestamp.to_be_bytes()[..]);
259 hasher.update(attributes.prev_randao.as_slice());
260 hasher.update(attributes.suggested_fee_recipient.as_slice());
261 if let Some(withdrawals) = &attributes.withdrawals {
262 let mut buf = Vec::new();
263 withdrawals.encode(&mut buf);
264 hasher.update(buf);
265 }
266
267 if let Some(parent_beacon_block) = attributes.parent_beacon_block_root {
268 hasher.update(parent_beacon_block);
269 }
270
271 let out = hasher.finalize();
272 PayloadId::new(out.as_slice()[..8].try_into().expect("sufficient length"))
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278 use alloy_eips::eip4895::Withdrawal;
279 use alloy_primitives::B64;
280 use core::str::FromStr;
281
282 #[test]
283 fn attributes_serde() {
284 let attributes = r#"{"timestamp":"0x1235","prevRandao":"0xf343b00e02dc34ec0124241f74f32191be28fb370bb48060f5fa4df99bda774c","suggestedFeeRecipient":"0x0000000000000000000000000000000000000000","withdrawals":null,"parentBeaconBlockRoot":null}"#;
285 let _attributes: PayloadAttributes = serde_json::from_str(attributes).unwrap();
286 }
287
288 #[test]
289 fn test_payload_id_basic() {
290 let parent =
292 B256::from_str("0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a")
293 .unwrap();
294 let attributes = PayloadAttributes {
295 timestamp: 0x5,
296 prev_randao: B256::from_str(
297 "0x0000000000000000000000000000000000000000000000000000000000000000",
298 )
299 .unwrap(),
300 suggested_fee_recipient: Address::from_str(
301 "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
302 )
303 .unwrap(),
304 withdrawals: None,
305 parent_beacon_block_root: None,
306 };
307
308 assert_eq!(
310 payload_id(&parent, &attributes),
311 PayloadId(B64::from_str("0xa247243752eb10b4").unwrap())
312 );
313 }
314
315 #[test]
316 fn test_payload_id_with_withdrawals() {
317 let parent =
319 B256::from_str("0x9876543210abcdef9876543210abcdef9876543210abcdef9876543210abcdef")
320 .unwrap();
321 let attributes = PayloadAttributes {
322 timestamp: 1622553200,
323 prev_randao: B256::from_slice(&[1; 32]),
324 suggested_fee_recipient: Address::from_str(
325 "0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b",
326 )
327 .unwrap(),
328 withdrawals: Some(vec![
329 Withdrawal {
330 index: 1,
331 validator_index: 123,
332 address: Address::from([0xAA; 20]),
333 amount: 10,
334 },
335 Withdrawal {
336 index: 2,
337 validator_index: 456,
338 address: Address::from([0xBB; 20]),
339 amount: 20,
340 },
341 ]),
342 parent_beacon_block_root: None,
343 };
344
345 assert_eq!(
347 payload_id(&parent, &attributes),
348 PayloadId(B64::from_str("0xedddc2f84ba59865").unwrap())
349 );
350 }
351
352 #[test]
353 fn test_payload_id_with_parent_beacon_block_root() {
354 let parent =
356 B256::from_str("0x9876543210abcdef9876543210abcdef9876543210abcdef9876543210abcdef")
357 .unwrap();
358 let attributes = PayloadAttributes {
359 timestamp: 1622553200,
360 prev_randao: B256::from_str(
361 "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
362 )
363 .unwrap(),
364 suggested_fee_recipient: Address::from_str(
365 "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
366 )
367 .unwrap(),
368 withdrawals: None,
369 parent_beacon_block_root: Some(
370 B256::from_str(
371 "0x2222222222222222222222222222222222222222222222222222222222222222",
372 )
373 .unwrap(),
374 ),
375 };
376
377 assert_eq!(
379 payload_id(&parent, &attributes),
380 PayloadId(B64::from_str("0x0fc49cd532094cce").unwrap())
381 );
382 }
383}