Skip to main content

ethrex_rpc/debug/
execution_witness.rs

1use ethrex_common::types::block_execution_witness::RpcExecutionWitness;
2use serde_json::Value;
3use tracing::debug;
4
5use crate::{RpcApiContext, RpcErr, RpcHandler, types::block_identifier::BlockIdentifier};
6
7pub struct ExecutionWitnessRequest {
8    pub from: BlockIdentifier,
9    pub to: Option<BlockIdentifier>,
10}
11
12impl RpcHandler for ExecutionWitnessRequest {
13    fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
14        let params = params
15            .as_ref()
16            .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
17        if params.len() > 2 {
18            return Err(RpcErr::BadParams(format!(
19                "Expected one or two params and {} were provided",
20                params.len()
21            )));
22        }
23
24        let from = BlockIdentifier::parse(params[0].clone(), 0)?;
25        let to = if let Some(param) = params.get(1) {
26            Some(BlockIdentifier::parse(param.clone(), 1)?)
27        } else {
28            None
29        };
30
31        Ok(ExecutionWitnessRequest { from, to })
32    }
33
34    async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
35        let from_block_number = self
36            .from
37            .resolve_block_number(&context.storage)
38            .await?
39            .ok_or(RpcErr::Internal(
40                "Failed to resolve block number".to_string(),
41            ))?;
42        let to_block_number = self
43            .to
44            .as_ref()
45            .unwrap_or(&self.from)
46            .resolve_block_number(&context.storage)
47            .await?
48            .ok_or(RpcErr::Internal(
49                "Failed to resolve block number".to_string(),
50            ))?;
51
52        if from_block_number > to_block_number {
53            return Err(RpcErr::BadParams(
54                "From block number is greater than To block number".to_string(),
55            ));
56        }
57
58        if self.to.is_some() {
59            debug!(
60                "Requested execution witness from block: {from_block_number} to {to_block_number}",
61            );
62        } else {
63            debug!("Requested execution witness for block: {from_block_number}",);
64        }
65
66        let mut blocks = Vec::new();
67        for block_number in from_block_number..=to_block_number {
68            let header = context
69                .storage
70                .get_block_header(block_number)?
71                .ok_or(RpcErr::Internal("Could not get block header".to_string()))?;
72            let block = context
73                .storage
74                .get_block_by_hash(header.hash())
75                .await?
76                .ok_or(RpcErr::Internal("Could not get block body".to_string()))?;
77            blocks.push(block);
78        }
79
80        if blocks.len() == 1 {
81            // Check if we have a cached witness for this block
82            // Use raw JSON bytes path to avoid deserialization + re-serialization
83            let block = &blocks[0];
84            if let Some(json_bytes) = context
85                .storage
86                .get_witness_json_bytes(block.header.number, block.hash())?
87            {
88                // Parse directly to Value - witness is already in RPC format
89                return serde_json::from_slice(&json_bytes)
90                    .map_err(|e| RpcErr::Internal(format!("Failed to parse cached witness: {e}")));
91            }
92        }
93
94        let execution_witness = context
95            .blockchain
96            .generate_witness_for_blocks(&blocks)
97            .await
98            .map_err(|e| RpcErr::Internal(format!("Failed to build execution witness {e}")))?;
99
100        let rpc_execution_witness = RpcExecutionWitness::try_from(execution_witness)
101            .map_err(|e| RpcErr::Internal(format!("Failed to create rpc execution witness {e}")))?;
102
103        serde_json::to_value(rpc_execution_witness)
104            .map_err(|error| RpcErr::Internal(error.to_string()))
105    }
106}