1use bytes::Bytes;
2use ethrex_blockchain::error::ChainError;
3use ethrex_blockchain::payload::PayloadBuildResult;
4use ethrex_common::types::block_access_list::BlockAccessList;
5use ethrex_common::types::block_execution_witness::{
6 ExecutionWitness, ExtWitness, RpcExecutionWitness,
7};
8use ethrex_common::types::payload::PayloadBundle;
9use ethrex_common::types::requests::{EncodedRequests, compute_requests_hash};
10use ethrex_common::types::{Block, BlockBody, BlockHash, BlockHeader, BlockNumber, Fork};
11use ethrex_common::{H256, U256};
12use ethrex_crypto::NativeCrypto;
13use ethrex_p2p::sync::SyncMode;
14use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError};
15use serde_json::Value;
16use tokio::sync::oneshot;
17use tracing::{debug, error, info, warn};
18
19use crate::rpc::{RpcApiContext, RpcHandler};
20use crate::types::payload::{
21 ExecutionPayload, ExecutionPayloadBody, ExecutionPayloadBodyV2, ExecutionPayloadResponse,
22 PayloadStatus,
23};
24use crate::utils::RpcErr;
25use crate::utils::{RpcRequest, parse_json_hex};
26
27const GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE: u64 = 1024;
31
32pub struct NewPayloadV1Request {
34 pub payload: ExecutionPayload,
35}
36
37impl RpcHandler for NewPayloadV1Request {
38 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
39 Ok(NewPayloadV1Request {
40 payload: parse_execution_payload(params)?,
41 })
42 }
43
44 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
45 validate_execution_payload_v1(&self.payload)?;
46 let block = match get_block_from_payload(&self.payload, None, None, None) {
47 Ok(block) => block,
48 Err(err) => {
49 return Ok(serde_json::to_value(PayloadStatus::invalid_with_err(
50 &err.to_string(),
51 ))?);
52 }
53 };
54 let payload_status =
55 handle_new_payload_v1_v2(&self.payload, block, context, None, false).await?;
56 serde_json::to_value(payload_status).map_err(|error| RpcErr::Internal(error.to_string()))
57 }
58}
59
60pub struct NewPayloadV2Request {
61 pub payload: ExecutionPayload,
62}
63
64impl RpcHandler for NewPayloadV2Request {
65 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
66 Ok(NewPayloadV2Request {
67 payload: parse_execution_payload(params)?,
68 })
69 }
70
71 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
72 let chain_config = &context.storage.get_chain_config();
73 if chain_config.is_shanghai_activated(self.payload.timestamp) {
74 validate_execution_payload_v2(&self.payload)?;
75 } else {
76 validate_execution_payload_v1(&self.payload)?;
78 }
79 let block = match get_block_from_payload(&self.payload, None, None, None) {
80 Ok(block) => block,
81 Err(err) => {
82 return Ok(serde_json::to_value(PayloadStatus::invalid_with_err(
83 &err.to_string(),
84 ))?);
85 }
86 };
87 let payload_status =
88 handle_new_payload_v1_v2(&self.payload, block, context, None, false).await?;
89 serde_json::to_value(payload_status).map_err(|error| RpcErr::Internal(error.to_string()))
90 }
91}
92
93pub struct NewPayloadV3Request {
94 pub payload: ExecutionPayload,
95 pub expected_blob_versioned_hashes: Vec<H256>,
96 pub parent_beacon_block_root: H256,
97}
98
99impl From<NewPayloadV3Request> for RpcRequest {
100 fn from(val: NewPayloadV3Request) -> Self {
101 RpcRequest {
102 method: "engine_newPayloadV3".to_string(),
103 params: Some(vec![
104 serde_json::json!(val.payload),
105 serde_json::json!(val.expected_blob_versioned_hashes),
106 serde_json::json!(val.parent_beacon_block_root),
107 ]),
108 ..Default::default()
109 }
110 }
111}
112
113impl RpcHandler for NewPayloadV3Request {
114 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
115 let params = params
116 .as_ref()
117 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
118 if params.len() != 3 {
119 return Err(RpcErr::BadParams("Expected 3 params".to_owned()));
120 }
121 Ok(NewPayloadV3Request {
122 payload: serde_json::from_value(params[0].clone())
123 .map_err(|_| RpcErr::WrongParam("payload".to_string()))?,
124 expected_blob_versioned_hashes: serde_json::from_value(params[1].clone())
125 .map_err(|_| RpcErr::WrongParam("expected_blob_versioned_hashes".to_string()))?,
126 parent_beacon_block_root: serde_json::from_value(params[2].clone())
127 .map_err(|_| RpcErr::WrongParam("parent_beacon_block_root".to_string()))?,
128 })
129 }
130
131 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
132 let block = match get_block_from_payload(
133 &self.payload,
134 Some(self.parent_beacon_block_root),
135 None,
136 None,
137 ) {
138 Ok(block) => block,
139 Err(err) => {
140 return Ok(serde_json::to_value(PayloadStatus::invalid_with_err(
141 &err.to_string(),
142 ))?);
143 }
144 };
145 validate_fork(&block, Fork::Cancun, &context)?;
146 validate_execution_payload_v3(&self.payload)?;
147 let payload_status = handle_new_payload_v3(
148 &self.payload,
149 context,
150 block,
151 self.expected_blob_versioned_hashes.clone(),
152 None,
153 false,
154 )
155 .await?;
156 serde_json::to_value(payload_status).map_err(|error| RpcErr::Internal(error.to_string()))
157 }
158}
159
160pub struct NewPayloadV4Request {
161 pub payload: ExecutionPayload,
162 pub expected_blob_versioned_hashes: Vec<H256>,
163 pub parent_beacon_block_root: H256,
164 pub execution_requests: Vec<EncodedRequests>,
165}
166
167impl From<NewPayloadV4Request> for RpcRequest {
168 fn from(val: NewPayloadV4Request) -> Self {
169 RpcRequest {
170 method: "engine_newPayloadV4".to_string(),
171 params: Some(vec![
172 serde_json::json!(val.payload),
173 serde_json::json!(val.expected_blob_versioned_hashes),
174 serde_json::json!(val.parent_beacon_block_root),
175 serde_json::json!(val.execution_requests),
176 ]),
177 ..Default::default()
178 }
179 }
180}
181
182impl RpcHandler for NewPayloadV4Request {
183 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
184 let params = params
185 .as_ref()
186 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
187 if params.len() != 4 {
188 return Err(RpcErr::BadParams("Expected 4 params".to_owned()));
189 }
190 Ok(NewPayloadV4Request {
191 payload: serde_json::from_value(params[0].clone())
192 .map_err(|_| RpcErr::WrongParam("payload".to_string()))?,
193 expected_blob_versioned_hashes: serde_json::from_value(params[1].clone())
194 .map_err(|_| RpcErr::WrongParam("expected_blob_versioned_hashes".to_string()))?,
195 parent_beacon_block_root: serde_json::from_value(params[2].clone())
196 .map_err(|_| RpcErr::WrongParam("parent_beacon_block_root".to_string()))?,
197 execution_requests: serde_json::from_value(params[3].clone())
198 .map_err(|_| RpcErr::WrongParam("execution_requests".to_string()))?,
199 })
200 }
201
202 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
203 if self.payload.block_access_list.is_some() {
207 return Err(RpcErr::WrongParam(
208 "block_access_list not allowed in engine_newPayloadV4".to_string(),
209 ));
210 }
211
212 validate_execution_requests(&self.execution_requests)?;
214
215 let requests_hash = compute_requests_hash(&self.execution_requests);
216 let block = match get_block_from_payload(
217 &self.payload,
218 Some(self.parent_beacon_block_root),
219 Some(requests_hash),
220 None,
221 ) {
222 Ok(block) => block,
223 Err(err) => {
224 return Ok(serde_json::to_value(PayloadStatus::invalid_with_err(
225 &err.to_string(),
226 ))?);
227 }
228 };
229
230 let chain_config = context.storage.get_chain_config();
231
232 if chain_config.is_amsterdam_activated(block.header.timestamp) {
237 return Err(RpcErr::UnsupportedFork(format!(
238 "{:?}",
239 chain_config.get_fork(block.header.timestamp)
240 )));
241 }
242
243 if !chain_config.is_prague_activated(block.header.timestamp) {
244 return Err(RpcErr::UnsupportedFork(format!(
245 "{:?}",
246 chain_config.get_fork(block.header.timestamp)
247 )));
248 }
249
250 validate_execution_payload_v3(&self.payload)?;
260 let payload_status = handle_new_payload_v3(
261 &self.payload,
262 context,
263 block,
264 self.expected_blob_versioned_hashes.clone(),
265 None,
266 false,
267 )
268 .await?;
269 serde_json::to_value(payload_status).map_err(|error| RpcErr::Internal(error.to_string()))
270 }
271}
272
273pub struct NewPayloadV5Request {
274 pub payload: ExecutionPayload,
275 pub expected_blob_versioned_hashes: Vec<H256>,
276 pub parent_beacon_block_root: H256,
277 pub execution_requests: Vec<EncodedRequests>,
278 pub raw_bal_hash: Option<H256>,
281}
282
283impl From<NewPayloadV5Request> for RpcRequest {
284 fn from(val: NewPayloadV5Request) -> Self {
285 RpcRequest {
286 method: "engine_newPayloadV5".to_string(),
287 params: Some(vec![
288 serde_json::json!(val.payload),
289 serde_json::json!(val.expected_blob_versioned_hashes),
290 serde_json::json!(val.parent_beacon_block_root),
291 serde_json::json!(val.execution_requests),
292 ]),
293 ..Default::default()
294 }
295 }
296}
297
298impl RpcHandler for NewPayloadV5Request {
299 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
300 let params = params
301 .as_ref()
302 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
303 if params.len() != 4 {
304 return Err(RpcErr::BadParams("Expected 4 params".to_owned()));
305 }
306
307 let raw_bal_hash = params[0]
311 .get("blockAccessList")
312 .map(|v| {
313 let hex_str = v
314 .as_str()
315 .ok_or(RpcErr::WrongParam("blockAccessList".to_string()))?;
316 let bytes = hex::decode(hex_str.trim_start_matches("0x"))
317 .map_err(|_| RpcErr::WrongParam("blockAccessList".to_string()))?;
318 Ok::<_, RpcErr>(ethrex_common::utils::keccak(bytes))
319 })
320 .transpose()?;
321
322 Ok(Self {
323 payload: serde_json::from_value(params[0].clone())
324 .map_err(|_| RpcErr::WrongParam("payload".to_string()))?,
325 expected_blob_versioned_hashes: serde_json::from_value(params[1].clone())
326 .map_err(|_| RpcErr::WrongParam("expected_blob_versioned_hashes".to_string()))?,
327 parent_beacon_block_root: serde_json::from_value(params[2].clone())
328 .map_err(|_| RpcErr::WrongParam("parent_beacon_block_root".to_string()))?,
329 execution_requests: serde_json::from_value(params[3].clone())
330 .map_err(|_| RpcErr::WrongParam("execution_requests".to_string()))?,
331 raw_bal_hash,
332 })
333 }
334
335 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
336 self.handle_with_witness(context, false).await
337 }
338}
339
340impl NewPayloadV5Request {
341 async fn handle_with_witness(
342 &self,
343 context: RpcApiContext,
344 make_witness: bool,
345 ) -> Result<Value, RpcErr> {
346 validate_execution_payload_v5(&self.payload)?;
347
348 validate_execution_requests(&self.execution_requests)?;
350
351 let requests_hash = compute_requests_hash(&self.execution_requests);
352 let block_access_list_hash = self.raw_bal_hash;
356
357 let block = match get_block_from_payload(
358 &self.payload,
359 Some(self.parent_beacon_block_root),
360 Some(requests_hash),
361 block_access_list_hash,
362 ) {
363 Ok(block) => block,
364 Err(err) => {
365 return Ok(serde_json::to_value(PayloadStatus::invalid_with_err(
366 &err.to_string(),
367 ))?);
368 }
369 };
370
371 let chain_config = context.storage.get_chain_config();
372
373 if !chain_config.is_amsterdam_activated(block.header.timestamp) {
378 return Err(RpcErr::UnsupportedFork(format!(
379 "{:?}",
380 chain_config.get_fork(block.header.timestamp)
381 )));
382 }
383
384 if block.hash() != self.payload.block_hash {
393 let mut alt_header = block.header.clone();
394 alt_header.block_access_list_hash = None;
395 let alt_hash = alt_header.compute_block_hash(ðrex_crypto::NativeCrypto);
396 if alt_hash == self.payload.block_hash {
397 return Err(RpcErr::WrongParam(
398 "engine_newPayloadV5 received header missing block_access_list_hash field"
399 .to_string(),
400 ));
401 }
402 }
403
404 let bal = self.payload.block_access_list.clone();
405 let payload_status = handle_new_payload_v4(
406 &self.payload,
407 context,
408 block,
409 self.expected_blob_versioned_hashes.clone(),
410 bal,
411 make_witness,
412 )
413 .await?;
414 serde_json::to_value(payload_status).map_err(|error| RpcErr::Internal(error.to_string()))
415 }
416}
417
418pub struct NewPayloadWithWitnessV5Request(pub NewPayloadV5Request);
419
420impl From<NewPayloadWithWitnessV5Request> for RpcRequest {
421 fn from(val: NewPayloadWithWitnessV5Request) -> Self {
422 RpcRequest {
423 method: "engine_newPayloadWithWitnessV5".to_string(),
424 params: Some(vec![
425 serde_json::json!(val.0.payload),
426 serde_json::json!(val.0.expected_blob_versioned_hashes),
427 serde_json::json!(val.0.parent_beacon_block_root),
428 serde_json::json!(val.0.execution_requests),
429 ]),
430 ..Default::default()
431 }
432 }
433}
434
435impl RpcHandler for NewPayloadWithWitnessV5Request {
436 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
437 NewPayloadV5Request::parse(params).map(Self)
438 }
439
440 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
441 self.0.handle_with_witness(context, true).await
442 }
443}
444
445pub struct GetPayloadV1Request {
447 pub payload_id: u64,
448}
449
450impl RpcHandler for GetPayloadV1Request {
451 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
452 let payload_id = parse_get_payload_request(params)?;
453 Ok(Self { payload_id })
454 }
455
456 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
457 let payload_bundle = get_payload(self.payload_id, &context).await?;
458 validate_payload_v1_v2(&payload_bundle.block, &context)?;
461
462 let response = ExecutionPayload::from_block(payload_bundle.block, None);
464
465 serde_json::to_value(response).map_err(|error| RpcErr::Internal(error.to_string()))
466 }
467}
468
469pub struct GetPayloadV2Request {
470 pub payload_id: u64,
471}
472
473impl RpcHandler for GetPayloadV2Request {
474 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
475 let payload_id = parse_get_payload_request(params)?;
476 Ok(Self { payload_id })
477 }
478
479 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
480 let payload_bundle = get_payload(self.payload_id, &context).await?;
481 validate_payload_v1_v2(&payload_bundle.block, &context)?;
482
483 let response = ExecutionPayloadResponse {
485 execution_payload: ExecutionPayload::from_block(payload_bundle.block, None),
486 block_value: payload_bundle.block_value,
487 blobs_bundle: None,
488 should_override_builder: None,
489 execution_requests: None,
490 };
491
492 serde_json::to_value(response).map_err(|error| RpcErr::Internal(error.to_string()))
493 }
494}
495
496pub struct GetPayloadV3Request {
497 pub payload_id: u64,
498}
499
500impl From<GetPayloadV3Request> for RpcRequest {
501 fn from(val: GetPayloadV3Request) -> Self {
502 RpcRequest {
503 method: "engine_getPayloadV3".to_string(),
504 params: Some(vec![serde_json::json!(U256::from(val.payload_id))]),
505 ..Default::default()
506 }
507 }
508}
509
510impl RpcHandler for GetPayloadV3Request {
511 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
512 let payload_id = parse_get_payload_request(params)?;
513 Ok(Self { payload_id })
514 }
515
516 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
517 let payload_bundle = get_payload(self.payload_id, &context).await?;
518 validate_fork(&payload_bundle.block, Fork::Cancun, &context)?;
519
520 let response = ExecutionPayloadResponse {
522 execution_payload: ExecutionPayload::from_block(payload_bundle.block, None),
523 block_value: payload_bundle.block_value,
524 blobs_bundle: Some(payload_bundle.blobs_bundle),
525 should_override_builder: Some(false),
526 execution_requests: None,
527 };
528
529 serde_json::to_value(response).map_err(|error| RpcErr::Internal(error.to_string()))
530 }
531}
532
533pub struct GetPayloadV4Request {
534 pub payload_id: u64,
535}
536
537impl From<GetPayloadV4Request> for RpcRequest {
538 fn from(val: GetPayloadV4Request) -> Self {
539 RpcRequest {
540 method: "engine_getPayloadV4".to_string(),
541 params: Some(vec![serde_json::json!(U256::from(val.payload_id))]),
542 ..Default::default()
543 }
544 }
545}
546
547impl RpcHandler for GetPayloadV4Request {
548 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
549 let payload_id = parse_get_payload_request(params)?;
550 Ok(Self { payload_id })
551 }
552
553 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
554 let payload_bundle = get_payload(self.payload_id, &context).await?;
555 let chain_config = &context.storage.get_chain_config();
556
557 if !chain_config.is_prague_activated(payload_bundle.block.header.timestamp) {
558 return Err(RpcErr::UnsupportedFork(format!(
559 "{:?}",
560 chain_config.get_fork(payload_bundle.block.header.timestamp)
561 )));
562 }
563 if chain_config.is_osaka_activated(payload_bundle.block.header.timestamp) {
564 return Err(RpcErr::UnsupportedFork(format!("{:?}", Fork::Osaka)));
565 }
566
567 let response = ExecutionPayloadResponse {
569 execution_payload: ExecutionPayload::from_block(payload_bundle.block, None),
570 block_value: payload_bundle.block_value,
571 blobs_bundle: Some(payload_bundle.blobs_bundle),
572 should_override_builder: Some(false),
573 execution_requests: Some(
574 payload_bundle
575 .requests
576 .into_iter()
577 .filter(|r| !r.is_empty())
578 .collect(),
579 ),
580 };
581
582 serde_json::to_value(response).map_err(|error| RpcErr::Internal(error.to_string()))
583 }
584}
585
586pub struct GetPayloadV5Request {
587 pub payload_id: u64,
588}
589
590impl From<GetPayloadV5Request> for RpcRequest {
591 fn from(val: GetPayloadV5Request) -> Self {
592 RpcRequest {
593 method: "engine_getPayloadV5".to_string(),
594 params: Some(vec![serde_json::json!(U256::from(val.payload_id))]),
595 ..Default::default()
596 }
597 }
598}
599
600impl RpcHandler for GetPayloadV5Request {
601 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
602 let payload_id = parse_get_payload_request(params)?;
603 Ok(Self { payload_id })
604 }
605
606 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
607 let payload_bundle = get_payload(self.payload_id, &context).await?;
608 let chain_config = &context.storage.get_chain_config();
609
610 if !chain_config.is_osaka_activated(payload_bundle.block.header.timestamp)
611 || chain_config.is_amsterdam_activated(payload_bundle.block.header.timestamp)
612 {
613 return Err(RpcErr::UnsupportedFork(format!(
614 "{:?}",
615 chain_config.get_fork(payload_bundle.block.header.timestamp)
616 )));
617 }
618
619 let response = ExecutionPayloadResponse {
621 execution_payload: ExecutionPayload::from_block(
622 payload_bundle.block,
623 payload_bundle.block_access_list,
624 ),
625 block_value: payload_bundle.block_value,
626 blobs_bundle: Some(payload_bundle.blobs_bundle),
627 should_override_builder: Some(false),
628 execution_requests: Some(
629 payload_bundle
630 .requests
631 .into_iter()
632 .filter(|r| !r.is_empty())
633 .collect(),
634 ),
635 };
636
637 serde_json::to_value(response).map_err(|error| RpcErr::Internal(error.to_string()))
638 }
639}
640
641pub struct GetPayloadV6Request {
642 pub payload_id: u64,
643}
644
645impl From<GetPayloadV6Request> for RpcRequest {
646 fn from(val: GetPayloadV6Request) -> Self {
647 RpcRequest {
648 method: "engine_getPayloadV6".to_string(),
649 params: Some(vec![serde_json::json!(U256::from(val.payload_id))]),
650 ..Default::default()
651 }
652 }
653}
654
655impl RpcHandler for GetPayloadV6Request {
656 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
657 let payload_id = parse_get_payload_request(params)?;
658 Ok(Self { payload_id })
659 }
660
661 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
662 let payload_bundle = get_payload(self.payload_id, &context).await?;
663 let chain_config = &context.storage.get_chain_config();
664
665 if !chain_config.is_amsterdam_activated(payload_bundle.block.header.timestamp) {
666 return Err(RpcErr::UnsupportedFork(format!(
667 "{:?}",
668 chain_config.get_fork(payload_bundle.block.header.timestamp)
669 )));
670 }
671
672 let response = ExecutionPayloadResponse {
674 execution_payload: ExecutionPayload::from_block(
675 payload_bundle.block,
676 payload_bundle.block_access_list,
677 ),
678 block_value: payload_bundle.block_value,
679 blobs_bundle: Some(payload_bundle.blobs_bundle),
680 should_override_builder: Some(false),
681 execution_requests: Some(
682 payload_bundle
683 .requests
684 .into_iter()
685 .filter(|r| !r.is_empty())
686 .collect(),
687 ),
688 };
689
690 serde_json::to_value(response).map_err(|error| RpcErr::Internal(error.to_string()))
691 }
692}
693
694pub struct GetPayloadBodiesByHashV1Request {
695 pub hashes: Vec<BlockHash>,
696}
697
698impl RpcHandler for GetPayloadBodiesByHashV1Request {
699 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
700 let params = params
701 .as_ref()
702 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
703 if params.len() != 1 {
704 return Err(RpcErr::BadParams("Expected 1 param".to_owned()));
705 };
706
707 Ok(GetPayloadBodiesByHashV1Request {
708 hashes: serde_json::from_value(params[0].clone())?,
709 })
710 }
711
712 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
713 if self.hashes.len() as u64 > GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE {
714 return Err(RpcErr::TooLargeRequest);
715 }
716 let mut bodies = Vec::new();
717 for hash in self.hashes.iter() {
718 bodies.push(context.storage.get_block_body_by_hash(*hash).await?)
719 }
720 build_payload_body_response(bodies)
721 }
722}
723
724pub struct GetPayloadBodiesByRangeV1Request {
725 start: BlockNumber,
726 count: u64,
727}
728
729impl RpcHandler for GetPayloadBodiesByRangeV1Request {
730 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
731 let params = params
732 .as_ref()
733 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
734 if params.len() != 2 {
735 return Err(RpcErr::BadParams("Expected 1 param".to_owned()));
736 };
737 let start = parse_json_hex(¶ms[0]).map_err(|_| RpcErr::BadHexFormat(0))?;
738 let count = parse_json_hex(¶ms[1]).map_err(|_| RpcErr::BadHexFormat(1))?;
739 if start < 1 {
740 return Err(RpcErr::WrongParam("start".to_owned()));
741 }
742 if count < 1 {
743 return Err(RpcErr::WrongParam("count".to_owned()));
744 }
745 Ok(GetPayloadBodiesByRangeV1Request { start, count })
746 }
747
748 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
749 if self.count > GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE {
750 return Err(RpcErr::TooLargeRequest);
751 }
752 let latest_block_number = context.storage.get_latest_block_number().await?;
753 let last = latest_block_number.min(self.start + self.count - 1);
756 let bodies = context.storage.get_block_bodies(self.start, last).await?;
757 build_payload_body_response(bodies)
758 }
759}
760
761fn build_payload_body_response(bodies: Vec<Option<BlockBody>>) -> Result<Value, RpcErr> {
762 let response: Vec<Option<ExecutionPayloadBody>> = bodies
763 .into_iter()
764 .map(|body| body.map(Into::into))
765 .collect();
766 serde_json::to_value(response).map_err(|error| RpcErr::Internal(error.to_string()))
767}
768
769fn bal_for_block(
776 context: &RpcApiContext,
777 block: &Block,
778) -> Result<Option<BlockAccessList>, RpcErr> {
779 let block_hash = block.hash();
780 let commitment = block.header.block_access_list_hash;
781 if let Some(bal) = context.storage.get_block_access_list(block_hash)? {
782 if bal.matches_commitment(commitment, &NativeCrypto) {
786 return Ok(Some(bal));
787 }
788 warn!("Stored BAL for {block_hash} does not match header commitment; ignoring it");
789 }
790 let generated = context
791 .blockchain
792 .generate_bal_for_block(block)
793 .map_err(|e| RpcErr::Internal(e.to_string()))?;
794 let regenerated = generated.is_some();
798 let Some(bal) = generated.filter(|bal| bal.matches_commitment(commitment, &NativeCrypto))
799 else {
800 if regenerated {
804 warn!("Regenerated BAL for {block_hash} does not match header commitment; discarding");
805 }
806 return Ok(None);
807 };
808 if let Err(err) = context.storage.store_block_access_list(block_hash, &bal) {
811 warn!("Failed to persist regenerated block access list for {block_hash}: {err}");
812 }
813 Ok(Some(bal))
814}
815
816pub struct GetPayloadBodiesByHashV2Request {
819 pub hashes: Vec<BlockHash>,
820}
821
822impl RpcHandler for GetPayloadBodiesByHashV2Request {
823 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
824 let params = params
825 .as_ref()
826 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
827 if params.len() != 1 {
828 return Err(RpcErr::BadParams("Expected 1 param".to_owned()));
829 };
830
831 Ok(GetPayloadBodiesByHashV2Request {
832 hashes: serde_json::from_value(params[0].clone())?,
833 })
834 }
835
836 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
837 if self.hashes.len() as u64 > GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE {
838 return Err(RpcErr::TooLargeRequest);
839 }
840
841 let mut bodies: Vec<Option<ExecutionPayloadBodyV2>> = Vec::new();
842 for hash in &self.hashes {
843 let block = context.storage.get_block_by_hash(*hash).await?;
844 let result = match block {
845 Some(block) => {
846 let bal = bal_for_block(&context, &block)?;
847 Some(ExecutionPayloadBodyV2::from_body_with_bal(block.body, bal))
848 }
849 None => None,
850 };
851 bodies.push(result);
852 }
853
854 serde_json::to_value(bodies).map_err(|e| RpcErr::Internal(e.to_string()))
855 }
856}
857
858pub struct GetPayloadBodiesByRangeV2Request {
859 start: BlockNumber,
860 count: u64,
861}
862
863impl RpcHandler for GetPayloadBodiesByRangeV2Request {
864 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
865 let params = params
866 .as_ref()
867 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
868 if params.len() != 2 {
869 return Err(RpcErr::BadParams("Expected 2 params".to_owned()));
870 };
871 let start = parse_json_hex(¶ms[0]).map_err(|_| RpcErr::BadHexFormat(0))?;
872 let count = parse_json_hex(¶ms[1]).map_err(|_| RpcErr::BadHexFormat(1))?;
873 if start < 1 {
874 return Err(RpcErr::WrongParam("start".to_owned()));
875 }
876 if count < 1 {
877 return Err(RpcErr::WrongParam("count".to_owned()));
878 }
879 Ok(GetPayloadBodiesByRangeV2Request { start, count })
880 }
881
882 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
883 if self.count > GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE {
884 return Err(RpcErr::TooLargeRequest);
885 }
886 let latest_block_number = context.storage.get_latest_block_number().await?;
887 let last = latest_block_number.min(self.start + self.count - 1);
890
891 let block_bodies = context.storage.get_block_bodies(self.start, last).await?;
893
894 let mut bodies: Vec<Option<ExecutionPayloadBodyV2>> = Vec::new();
895 for (i, body_opt) in block_bodies.into_iter().enumerate() {
896 let block_number = self.start + i as u64;
897 let result = match body_opt {
898 Some(body) => {
899 let header =
901 context
902 .storage
903 .get_block_header(block_number)?
904 .ok_or_else(|| {
905 RpcErr::Internal(format!(
906 "Header not found for block {block_number}"
907 ))
908 })?;
909 let block = Block { header, body };
910
911 let bal = bal_for_block(&context, &block)?;
912 Some(ExecutionPayloadBodyV2::from_body_with_bal(block.body, bal))
913 }
914 None => None,
915 };
916 bodies.push(result);
917 }
918
919 serde_json::to_value(bodies).map_err(|e| RpcErr::Internal(e.to_string()))
920 }
921}
922
923fn parse_execution_payload(params: &Option<Vec<Value>>) -> Result<ExecutionPayload, RpcErr> {
924 let params = params
925 .as_ref()
926 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
927 if params.len() != 1 {
928 return Err(RpcErr::BadParams("Expected 1 param".to_owned()));
929 }
930 serde_json::from_value(params[0].clone()).map_err(|_| RpcErr::WrongParam("payload".to_string()))
931}
932
933fn validate_execution_payload_v1(payload: &ExecutionPayload) -> Result<(), RpcErr> {
934 if payload.withdrawals.is_some() {
936 return Err(RpcErr::WrongParam("withdrawals".to_string()));
937 }
938 if payload.blob_gas_used.is_some() {
939 return Err(RpcErr::WrongParam("blob_gas_used".to_string()));
940 }
941 if payload.excess_blob_gas.is_some() {
942 return Err(RpcErr::WrongParam("excess_blob_gas".to_string()));
943 }
944
945 Ok(())
946}
947
948fn validate_execution_payload_v2(payload: &ExecutionPayload) -> Result<(), RpcErr> {
949 if payload.withdrawals.is_none() {
951 return Err(RpcErr::WrongParam("withdrawals".to_string()));
952 }
953 if payload.blob_gas_used.is_some() {
954 return Err(RpcErr::WrongParam("blob_gas_used".to_string()));
955 }
956 if payload.excess_blob_gas.is_some() {
957 return Err(RpcErr::WrongParam("excess_blob_gas".to_string()));
958 }
959
960 Ok(())
961}
962
963fn validate_execution_payload_v3(payload: &ExecutionPayload) -> Result<(), RpcErr> {
964 if payload.withdrawals.is_none() {
966 return Err(RpcErr::WrongParam("withdrawals".to_string()));
967 }
968 if payload.blob_gas_used.is_none() {
969 return Err(RpcErr::WrongParam("blob_gas_used".to_string()));
970 }
971 if payload.excess_blob_gas.is_none() {
972 return Err(RpcErr::WrongParam("excess_blob_gas".to_string()));
973 }
974
975 Ok(())
976}
977
978#[inline]
979fn validate_execution_payload_v4(payload: &ExecutionPayload) -> Result<(), RpcErr> {
980 if payload.block_access_list.is_none() {
984 return Err(RpcErr::WrongParam("block_access_list".to_string()));
985 }
986
987 validate_execution_payload_v3(payload)?;
988
989 Ok(())
990}
991
992#[inline]
993fn validate_execution_payload_v5(payload: &ExecutionPayload) -> Result<(), RpcErr> {
994 validate_execution_payload_v4(payload)?;
995
996 if payload.slot_number.is_none() {
997 return Err(RpcErr::WrongParam("slot_number".to_string()));
998 }
999
1000 Ok(())
1001}
1002
1003fn validate_payload_v1_v2(block: &Block, context: &RpcApiContext) -> Result<(), RpcErr> {
1004 let chain_config = &context.storage.get_chain_config();
1005 if chain_config.is_cancun_activated(block.header.timestamp) {
1006 return Err(RpcErr::UnsupportedFork(
1007 "Cancun payload received".to_string(),
1008 ));
1009 }
1010 Ok(())
1011}
1012
1013async fn validate_ancestors(
1015 block: &Block,
1016 context: &RpcApiContext,
1017) -> Result<Option<PayloadStatus>, RpcErr> {
1018 if let Some(latest_valid_hash) = context
1020 .storage
1021 .get_latest_valid_ancestor(block.hash())
1022 .await?
1023 {
1024 return Ok(Some(PayloadStatus::invalid_with(
1025 latest_valid_hash,
1026 "Header has been previously invalidated.".into(),
1027 )));
1028 }
1029
1030 if let Some(latest_valid_hash) = context
1032 .storage
1033 .get_latest_valid_ancestor(block.header.parent_hash)
1034 .await?
1035 {
1036 context
1038 .storage
1039 .set_latest_valid_ancestor(block.header.hash(), latest_valid_hash)
1040 .await?;
1041 return Ok(Some(PayloadStatus::invalid_with(
1042 latest_valid_hash,
1043 "Parent header has been previously invalidated.".into(),
1044 )));
1045 }
1046
1047 Ok(None)
1048}
1049
1050async fn handle_new_payload_v1_v2(
1051 payload: &ExecutionPayload,
1052 block: Block,
1053 context: RpcApiContext,
1054 bal: Option<BlockAccessList>,
1055 make_witness: bool,
1056) -> Result<PayloadStatus, RpcErr> {
1057 let Some(syncer) = &context.syncer else {
1058 return Err(RpcErr::Internal(
1059 "New payload requested but syncer is not initialized".to_string(),
1060 ));
1061 };
1062 if let Err(RpcErr::Internal(error_msg)) = validate_block_hash(payload, &block) {
1064 return Ok(PayloadStatus::invalid_with_err(&error_msg));
1065 }
1066
1067 if let Some(status) = validate_ancestors(&block, &context).await? {
1069 return Ok(status);
1070 }
1071
1072 let latest_valid_hash = block.header.parent_hash;
1074
1075 if syncer.sync_mode() == SyncMode::Snap {
1076 debug!("Snap sync in progress, skipping new payload validation");
1077 return Ok(PayloadStatus::syncing());
1078 }
1079
1080 let payload_status =
1082 try_execute_payload(block, &context, latest_valid_hash, bal, make_witness).await?;
1083 Ok(payload_status)
1084}
1085
1086async fn handle_new_payload_v3(
1087 payload: &ExecutionPayload,
1088 context: RpcApiContext,
1089 block: Block,
1090 expected_blob_versioned_hashes: Vec<H256>,
1091 bal: Option<BlockAccessList>,
1092 make_witness: bool,
1093) -> Result<PayloadStatus, RpcErr> {
1094 let blob_versioned_hashes: Vec<H256> = block
1096 .body
1097 .transactions
1098 .iter()
1099 .flat_map(|tx| tx.blob_versioned_hashes())
1100 .collect();
1101
1102 if expected_blob_versioned_hashes != blob_versioned_hashes {
1103 return Ok(PayloadStatus::invalid_with_err(
1104 "Invalid blob_versioned_hashes",
1105 ));
1106 }
1107
1108 handle_new_payload_v1_v2(payload, block, context, bal, make_witness).await
1109}
1110
1111async fn handle_new_payload_v4(
1112 payload: &ExecutionPayload,
1113 context: RpcApiContext,
1114 block: Block,
1115 expected_blob_versioned_hashes: Vec<H256>,
1116 bal: Option<BlockAccessList>,
1117 make_witness: bool,
1118) -> Result<PayloadStatus, RpcErr> {
1119 if let Some(bal) = &bal
1120 && let Err(err) = bal.validate_ordering()
1121 {
1122 return Ok(PayloadStatus::invalid_with_err(&err));
1123 }
1124 handle_new_payload_v3(
1125 payload,
1126 context,
1127 block,
1128 expected_blob_versioned_hashes,
1129 bal,
1130 make_witness,
1131 )
1132 .await
1133}
1134
1135fn validate_execution_requests(execution_requests: &[EncodedRequests]) -> Result<(), RpcErr> {
1138 let mut last_type: i32 = -1;
1139 for requests in execution_requests {
1140 if requests.0.len() < 2 {
1141 return Err(RpcErr::WrongParam("Empty requests data.".to_string()));
1142 }
1143 let request_type = requests.0[0] as i32;
1144 if last_type >= request_type {
1145 return Err(RpcErr::WrongParam("Invalid requests order.".to_string()));
1146 }
1147 last_type = request_type;
1148 }
1149 Ok(())
1150}
1151
1152fn get_block_from_payload(
1153 payload: &ExecutionPayload,
1154 parent_beacon_block_root: Option<H256>,
1155 requests_hash: Option<H256>,
1156 block_access_list_hash: Option<H256>,
1157) -> Result<Block, RLPDecodeError> {
1158 let block_hash = payload.block_hash;
1159 let block_number = payload.block_number;
1160 debug!(%block_hash, %block_number, "Received new payload");
1161
1162 payload.clone().into_block(
1163 parent_beacon_block_root,
1164 requests_hash,
1165 block_access_list_hash,
1166 )
1167}
1168
1169fn validate_block_hash(payload: &ExecutionPayload, block: &Block) -> Result<(), RpcErr> {
1170 let block_hash = payload.block_hash;
1171 let actual_block_hash = block.hash();
1172 if block_hash != actual_block_hash {
1173 return Err(RpcErr::Internal(format!(
1174 "Invalid block hash. Expected {actual_block_hash:#x}, got {block_hash:#x}"
1175 )));
1176 }
1177 Ok(())
1178}
1179
1180pub async fn add_block(
1181 ctx: &RpcApiContext,
1182 block: Block,
1183 bal: Option<BlockAccessList>,
1184 make_witness: bool,
1185) -> Result<Option<ExecutionWitness>, ChainError> {
1186 let (notify_send, notify_recv) = oneshot::channel();
1187 ctx.block_worker_channel
1188 .send((notify_send, block, bal, make_witness))
1189 .map_err(|e| {
1190 ChainError::Custom(format!(
1191 "failed to send block execution request to worker: {e}"
1192 ))
1193 })?;
1194 notify_recv
1195 .await
1196 .map_err(|e| ChainError::Custom(format!("failed to receive block execution result: {e}")))?
1197}
1198
1199async fn try_execute_payload(
1200 block: Block,
1201 context: &RpcApiContext,
1202 latest_valid_hash: H256,
1203 bal: Option<BlockAccessList>,
1204 make_witness: bool,
1205) -> Result<PayloadStatus, RpcErr> {
1206 let Some(syncer) = &context.syncer else {
1207 return Err(RpcErr::Internal(
1208 "New payload requested but syncer is not initialized".to_string(),
1209 ));
1210 };
1211 let block_hash = block.hash();
1212 let block_number = block.header.number;
1213 let storage = &context.storage;
1214 if storage.get_block_header_by_hash(block_hash)?.is_some() {
1219 return payload_status_for_existing_block(&block, context, make_witness).await;
1220 }
1221
1222 if let Some(parent_header) = storage.get_block_header_by_hash(block.header.parent_hash)?
1232 && !storage.has_state_root(parent_header.state_root)?
1233 {
1234 debug!(%block_hash, %block_number, "Parent state missing, returning SYNCING and triggering sync");
1235 syncer.sync_to_head(block_hash);
1236 return Ok(PayloadStatus::syncing());
1237 }
1238
1239 debug!(%block_hash, %block_number, "Executing payload");
1241
1242 match add_block(context, block, bal, make_witness).await {
1243 Err(ChainError::ParentNotFound) => {
1244 syncer.sync_to_head(block_hash);
1246 Ok(PayloadStatus::syncing())
1247 }
1248 Err(ChainError::ParentStateNotFound) => {
1253 debug!(%block_hash, "Parent state not found, returning SYNCING and triggering sync");
1254 syncer.sync_to_head(block_hash);
1255 Ok(PayloadStatus::syncing())
1256 }
1257 Err(ChainError::InvalidBlock(error)) => {
1258 warn!(%block_hash, %block_number, "Error executing block: {error}");
1259 context
1260 .storage
1261 .set_latest_valid_ancestor(block_hash, latest_valid_hash)
1262 .await?;
1263 Ok(PayloadStatus::invalid_with(
1264 latest_valid_hash,
1265 error.to_string(),
1266 ))
1267 }
1268 Err(ChainError::EvmError(error)) => {
1269 warn!(%block_hash, %block_number, "Error executing block: {error}");
1270 context
1271 .storage
1272 .set_latest_valid_ancestor(block_hash, latest_valid_hash)
1273 .await?;
1274 Ok(PayloadStatus::invalid_with(
1275 latest_valid_hash,
1276 error.to_string(),
1277 ))
1278 }
1279 Err(ChainError::StoreError(error)) => {
1280 warn!(%block_hash, %block_number, "Error storing block: {error}");
1281 Err(RpcErr::Internal(error.to_string()))
1282 }
1283 Err(e) => {
1284 error!("{e} for block {block_hash}");
1285 Err(RpcErr::Internal(e.to_string()))
1286 }
1287 Ok(witness) => {
1288 debug!("Block with hash {block_hash} executed and added to storage successfully");
1289 let mut status = PayloadStatus::valid_with_hash(block_hash);
1290 if make_witness {
1291 let witness = witness.ok_or_else(|| {
1292 RpcErr::Internal("Payload executed without producing a witness".to_string())
1293 })?;
1294 status.witness = Some(encode_witness_for_engine_rpc(witness)?);
1295 }
1296 Ok(status)
1297 }
1298 }
1299}
1300
1301async fn payload_status_for_existing_block(
1302 block: &Block,
1303 context: &RpcApiContext,
1304 make_witness: bool,
1305) -> Result<PayloadStatus, RpcErr> {
1306 let block_hash = block.hash();
1307 let mut status = PayloadStatus::valid_with_hash(block_hash);
1308
1309 if make_witness {
1310 status.witness = Some(witness_for_existing_block(block, context).await?);
1311 }
1312
1313 Ok(status)
1314}
1315
1316async fn witness_for_existing_block(
1317 block: &Block,
1318 context: &RpcApiContext,
1319) -> Result<Bytes, RpcErr> {
1320 let block_hash = block.hash();
1321 if let Some(json_bytes) = context
1322 .storage
1323 .get_witness_json_bytes(block.header.number, block_hash)?
1324 {
1325 let rpc_witness = serde_json::from_slice(&json_bytes).map_err(|error| {
1326 RpcErr::Internal(format!("Failed to parse cached witness: {error}"))
1327 })?;
1328 return encode_rpc_witness_for_engine_rpc(rpc_witness);
1329 }
1330
1331 let witness = context
1332 .blockchain
1333 .generate_witness_for_blocks(std::slice::from_ref(block))
1334 .await
1335 .map_err(|error| RpcErr::Internal(format!("Failed to build execution witness: {error}")))?;
1336 encode_witness_for_engine_rpc(witness)
1337}
1338
1339fn encode_witness_for_engine_rpc(witness: ExecutionWitness) -> Result<Bytes, RpcErr> {
1340 let rpc_witness = RpcExecutionWitness::try_from(witness).map_err(|error| {
1341 RpcErr::Internal(format!("Failed to encode execution witness: {error}"))
1342 })?;
1343 encode_rpc_witness_for_engine_rpc(rpc_witness)
1344}
1345
1346fn encode_rpc_witness_for_engine_rpc(rpc_witness: RpcExecutionWitness) -> Result<Bytes, RpcErr> {
1355 let mut headers = rpc_witness
1356 .headers
1357 .iter()
1358 .map(|header| BlockHeader::decode(header.as_ref()))
1359 .collect::<Result<Vec<_>, _>>()
1360 .map_err(|error| RpcErr::Internal(format!("Failed to decode witness header: {error}")))?;
1361 headers.sort_by_key(|header| header.number);
1362 let mut codes = rpc_witness.codes;
1363 codes.sort_by(|a, b| a.as_ref().cmp(b.as_ref()));
1364 let mut state = rpc_witness.state;
1365 state.sort_by(|a, b| a.as_ref().cmp(b.as_ref()));
1366 let mut keys = rpc_witness.keys;
1367 keys.sort_by(|a, b| a.as_ref().cmp(b.as_ref()));
1368 let ext_witness = ExtWitness {
1369 headers,
1370 codes,
1371 state,
1372 keys,
1373 };
1374 Ok(Bytes::from(ext_witness.encode_to_vec()))
1375}
1376
1377fn parse_get_payload_request(params: &Option<Vec<Value>>) -> Result<u64, RpcErr> {
1378 let params = params
1379 .as_ref()
1380 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
1381 if params.len() != 1 {
1382 return Err(RpcErr::BadParams("Expected 1 param".to_owned()));
1383 };
1384 let Ok(hex_str) = serde_json::from_value::<String>(params[0].clone()) else {
1385 return Err(RpcErr::BadParams(
1386 "Expected param to be a string".to_owned(),
1387 ));
1388 };
1389 let Some(hex_str) = hex_str.strip_prefix("0x") else {
1391 return Err(RpcErr::BadHexFormat(0));
1392 };
1393 let Ok(payload_id) = u64::from_str_radix(hex_str, 16) else {
1395 return Err(RpcErr::BadHexFormat(0));
1396 };
1397 Ok(payload_id)
1398}
1399
1400fn validate_fork(block: &Block, fork: Fork, context: &RpcApiContext) -> Result<(), RpcErr> {
1401 let chain_config = &context.storage.get_chain_config();
1403 let current_fork = chain_config.get_fork(block.header.timestamp);
1404
1405 if current_fork != fork {
1406 return Err(RpcErr::UnsupportedFork(format!("{current_fork:?}")));
1407 }
1408 Ok(())
1409}
1410
1411async fn get_payload(payload_id: u64, context: &RpcApiContext) -> Result<PayloadBundle, RpcErr> {
1412 info!(
1413 id = %format!("{:#018x}", payload_id),
1414 "Requested payload with"
1415 );
1416 let (blobs_bundle, requests, block_value, block, block_access_list) = {
1417 let PayloadBuildResult {
1418 blobs_bundle,
1419 block_value,
1420 requests,
1421 payload,
1422 block_access_list,
1423 ..
1424 } = context
1425 .blockchain
1426 .get_payload(payload_id)
1427 .await
1428 .map_err(|err| match err {
1429 ChainError::UnknownPayload => {
1430 RpcErr::UnknownPayload(format!("Payload with id {payload_id:#018x} not found",))
1431 }
1432 err => RpcErr::Internal(err.to_string()),
1433 })?;
1434 (
1435 blobs_bundle,
1436 requests,
1437 block_value,
1438 payload,
1439 block_access_list,
1440 )
1441 };
1442
1443 let new_payload = PayloadBundle {
1444 block,
1445 block_value,
1446 blobs_bundle,
1447 requests,
1448 block_access_list,
1449 };
1450
1451 Ok(new_payload)
1452}
1453
1454#[cfg(test)]
1455mod tests {
1456 use super::*;
1457 use crate::test_utils::default_context_with_storage;
1458 use ethrex_common::types::ChainConfig;
1459 use ethrex_rlp::encode::RLPEncode;
1460 use ethrex_storage::{EngineType, Store};
1461
1462 fn header(number: u64) -> BlockHeader {
1463 BlockHeader {
1464 number,
1465 ..Default::default()
1466 }
1467 }
1468
1469 fn v5_payload() -> ExecutionPayload {
1470 ExecutionPayload {
1471 parent_hash: H256::zero(),
1472 fee_recipient: Default::default(),
1473 state_root: H256::zero(),
1474 receipts_root: H256::zero(),
1475 logs_bloom: Default::default(),
1476 prev_randao: H256::zero(),
1477 block_number: 0,
1478 gas_limit: 30_000_000,
1479 gas_used: 0,
1480 timestamp: 0,
1481 extra_data: Bytes::new(),
1482 base_fee_per_gas: 1,
1483 block_hash: H256::zero(),
1484 transactions: vec![],
1485 withdrawals: Some(vec![]),
1486 blob_gas_used: Some(0),
1487 excess_blob_gas: Some(0),
1488 slot_number: Some(0),
1489 block_access_list: Some(BlockAccessList::default()),
1490 }
1491 }
1492
1493 #[test]
1494 fn new_payload_with_witness_v5_parses_like_v5() {
1495 let params = Some(vec![
1496 serde_json::json!(v5_payload()),
1497 serde_json::json!(Vec::<H256>::new()),
1498 serde_json::json!(H256::zero()),
1499 serde_json::json!(Vec::<EncodedRequests>::new()),
1500 ]);
1501
1502 let request = NewPayloadWithWitnessV5Request::parse(¶ms).unwrap();
1503
1504 assert_eq!(request.0.payload.slot_number, Some(0));
1505 assert!(request.0.raw_bal_hash.is_some());
1506 }
1507
1508 #[test]
1509 fn new_payload_v5_rejects_missing_slot_number() {
1510 let mut payload = v5_payload();
1511 payload.slot_number = None;
1512
1513 let err = validate_execution_payload_v5(&payload).unwrap_err();
1514
1515 assert!(matches!(err, RpcErr::WrongParam(param) if param == "slot_number"));
1516 }
1517
1518 #[test]
1519 fn engine_witness_encoding_matches_geth_ext_witness_shape() {
1520 let header_1 = header(1);
1521 let header_2 = header(2);
1522 let witness = RpcExecutionWitness {
1523 headers: vec![
1524 header_2.encode_to_vec().into(),
1525 header_1.encode_to_vec().into(),
1526 ],
1527 codes: vec![
1528 Bytes::from_static(&[0x02]),
1529 Bytes::from_static(&[0x01, 0xff]),
1530 Bytes::from_static(&[0x01]),
1531 ],
1532 state: vec![
1533 Bytes::from_static(&[0xff]),
1534 Bytes::from_static(&[0x00]),
1535 Bytes::from_static(&[0x7f]),
1536 ],
1537 keys: vec![Bytes::from_static(&[0x03]), Bytes::from_static(&[0x02])],
1538 };
1539
1540 let encoded = encode_rpc_witness_for_engine_rpc(witness).unwrap();
1541
1542 let expected_headers = vec![header_1, header_2];
1543 let expected_codes = vec![
1544 Bytes::from_static(&[0x01]),
1545 Bytes::from_static(&[0x01, 0xff]),
1546 Bytes::from_static(&[0x02]),
1547 ];
1548 let expected_state = vec![
1549 Bytes::from_static(&[0x00]),
1550 Bytes::from_static(&[0x7f]),
1551 Bytes::from_static(&[0xff]),
1552 ];
1553 let expected_keys = vec![Bytes::from_static(&[0x02]), Bytes::from_static(&[0x03])];
1554 let expected = (
1555 expected_headers,
1556 expected_codes,
1557 expected_state,
1558 expected_keys,
1559 )
1560 .encode_to_vec();
1561
1562 assert_eq!(encoded.as_ref(), expected.as_slice());
1563 }
1564
1565 #[test]
1566 fn engine_witness_encoding_from_execution_witness_matches_expected_bytes() {
1567 let header_1 = header(1);
1568 let header_2 = header(2);
1569 let witness = ExecutionWitness {
1570 codes: vec![vec![0x02], vec![0x01]],
1571 block_headers_bytes: vec![header_2.encode_to_vec(), header_1.encode_to_vec()],
1572 first_block_number: 1,
1573 chain_config: ChainConfig::default(),
1574 state_trie_root: None,
1575 storage_trie_roots: Default::default(),
1576 };
1577
1578 let encoded = encode_witness_for_engine_rpc(witness).unwrap();
1579
1580 let expected = (
1581 vec![header_1, header_2],
1582 vec![Bytes::from_static(&[0x01]), Bytes::from_static(&[0x02])],
1583 Vec::<Bytes>::new(),
1584 Vec::<Bytes>::new(),
1585 )
1586 .encode_to_vec();
1587 assert_eq!(encoded.as_ref(), expected.as_slice());
1588 }
1589
1590 async fn test_context() -> RpcApiContext {
1591 let storage = Store::new("test-payload-bodies", EngineType::InMemory)
1592 .expect("Failed to create test store");
1593 default_context_with_storage(storage).await
1594 }
1595
1596 #[tokio::test]
1597 async fn get_payload_bodies_by_hash_v1_accepts_exactly_max_size() {
1598 let request = GetPayloadBodiesByHashV1Request {
1601 hashes: vec![BlockHash::default(); GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE as usize],
1602 };
1603 let result = request.handle(test_context().await).await;
1604 assert!(!matches!(result, Err(RpcErr::TooLargeRequest)));
1605 }
1606
1607 #[tokio::test]
1608 async fn get_payload_bodies_by_hash_v1_rejects_above_max_size() {
1609 let request = GetPayloadBodiesByHashV1Request {
1610 hashes: vec![BlockHash::default(); GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE as usize + 1],
1611 };
1612 let result = request.handle(test_context().await).await;
1613 assert!(matches!(result, Err(RpcErr::TooLargeRequest)));
1614 }
1615
1616 #[tokio::test]
1617 async fn get_payload_bodies_by_range_v1_accepts_exactly_max_size() {
1618 let request = GetPayloadBodiesByRangeV1Request {
1619 start: 1,
1620 count: GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE,
1621 };
1622 let result = request.handle(test_context().await).await;
1623 assert!(!matches!(result, Err(RpcErr::TooLargeRequest)));
1624 }
1625
1626 #[tokio::test]
1627 async fn get_payload_bodies_by_range_v1_rejects_above_max_size() {
1628 let request = GetPayloadBodiesByRangeV1Request {
1629 start: 1,
1630 count: GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE + 1,
1631 };
1632 let result = request.handle(test_context().await).await;
1633 assert!(matches!(result, Err(RpcErr::TooLargeRequest)));
1634 }
1635
1636 #[tokio::test]
1637 async fn get_payload_bodies_by_hash_v2_accepts_exactly_max_size() {
1638 let request = GetPayloadBodiesByHashV2Request {
1639 hashes: vec![BlockHash::default(); (GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE) as usize],
1640 };
1641 let result = request.handle(test_context().await).await;
1642 assert!(!matches!(result, Err(RpcErr::TooLargeRequest)));
1643 }
1644
1645 #[tokio::test]
1646 async fn get_payload_bodies_by_hash_v2_rejects_above_max_size() {
1647 let request = GetPayloadBodiesByHashV2Request {
1648 hashes: vec![BlockHash::default(); (GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE + 1) as usize],
1649 };
1650 let result = request.handle(test_context().await).await;
1651 assert!(matches!(result, Err(RpcErr::TooLargeRequest)));
1652 }
1653
1654 #[tokio::test]
1655 async fn get_payload_bodies_by_range_v2_accepts_exactly_max_size() {
1656 let request = GetPayloadBodiesByRangeV2Request {
1657 start: 1,
1658 count: GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE,
1659 };
1660 let result = request.handle(test_context().await).await;
1661 assert!(!matches!(result, Err(RpcErr::TooLargeRequest)));
1662 }
1663
1664 #[tokio::test]
1665 async fn get_payload_bodies_by_range_v2_rejects_above_max_size() {
1666 let request = GetPayloadBodiesByRangeV2Request {
1667 start: 1,
1668 count: GET_PAYLOAD_BODIES_REQUEST_MAX_SIZE + 1,
1669 };
1670 let result = request.handle(test_context().await).await;
1671 assert!(matches!(result, Err(RpcErr::TooLargeRequest)));
1672 }
1673}