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