use ethrex_common::types::block_execution_witness::RpcExecutionWitness;
use serde_json::Value;
use tracing::debug;
use crate::{RpcApiContext, RpcErr, RpcHandler, types::block_identifier::BlockIdentifier};
pub struct ExecutionWitnessRequest {
pub from: BlockIdentifier,
pub to: Option<BlockIdentifier>,
}
impl RpcHandler for ExecutionWitnessRequest {
fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
let params = params
.as_ref()
.ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
if params.len() > 2 {
return Err(RpcErr::BadParams(format!(
"Expected one or two params and {} were provided",
params.len()
)));
}
let from = BlockIdentifier::parse(params[0].clone(), 0)?;
let to = if let Some(param) = params.get(1) {
Some(BlockIdentifier::parse(param.clone(), 1)?)
} else {
None
};
Ok(ExecutionWitnessRequest { from, to })
}
async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
let from_block_number = self
.from
.resolve_block_number(&context.storage)
.await?
.ok_or(RpcErr::Internal(
"Failed to resolve block number".to_string(),
))?;
let to_block_number = self
.to
.as_ref()
.unwrap_or(&self.from)
.resolve_block_number(&context.storage)
.await?
.ok_or(RpcErr::Internal(
"Failed to resolve block number".to_string(),
))?;
if from_block_number > to_block_number {
return Err(RpcErr::BadParams(
"From block number is greater than To block number".to_string(),
));
}
if self.to.is_some() {
debug!(
"Requested execution witness from block: {from_block_number} to {to_block_number}",
);
} else {
debug!("Requested execution witness for block: {from_block_number}",);
}
let mut blocks = Vec::new();
for block_number in from_block_number..=to_block_number {
let header = context
.storage
.get_block_header(block_number)?
.ok_or(RpcErr::Internal("Could not get block header".to_string()))?;
let block = context
.storage
.get_block_by_hash(header.hash())
.await?
.ok_or(RpcErr::Internal("Could not get block body".to_string()))?;
blocks.push(block);
}
if blocks.len() == 1 {
let block = &blocks[0];
if let Some(json_bytes) = context
.storage
.get_witness_json_bytes(block.header.number, block.hash())?
{
return serde_json::from_slice(&json_bytes)
.map_err(|e| RpcErr::Internal(format!("Failed to parse cached witness: {e}")));
}
}
let execution_witness = context
.blockchain
.generate_witness_for_blocks(&blocks)
.await
.map_err(|e| RpcErr::Internal(format!("Failed to build execution witness {e}")))?;
let rpc_execution_witness = RpcExecutionWitness::try_from(execution_witness)
.map_err(|e| RpcErr::Internal(format!("Failed to create rpc execution witness {e}")))?;
serde_json::to_value(rpc_execution_witness)
.map_err(|error| RpcErr::Internal(error.to_string()))
}
}