1#[cfg(test)]
11mod tests;
12
13use crate::{api::dev::error::Error, check_if_safe};
14use jsonrpsee::Extensions;
15use soil_client::client_api::{BlockBackend, HeaderBackend};
16use std::{
17 marker::{PhantomData, Send, Sync},
18 sync::Arc,
19};
20use subsoil::api::{ApiExt, Core, ProvideRuntimeApi};
21use subsoil::core::Encode;
22use subsoil::runtime::{
23 generic::DigestItem,
24 traits::{Block as BlockT, Header},
25};
26
27pub use crate::api::dev::{BlockStats, DevApiServer};
28
29type HasherOf<Block> = <<Block as BlockT>::Header as Header>::Hashing;
30
31pub struct Dev<Block: BlockT, Client> {
33 client: Arc<Client>,
34 _phantom: PhantomData<Block>,
35}
36
37impl<Block: BlockT, Client> Dev<Block, Client> {
38 pub fn new(client: Arc<Client>) -> Self {
40 Self { client, _phantom: PhantomData::default() }
41 }
42}
43
44impl<Block, Client> DevApiServer<Block::Hash> for Dev<Block, Client>
45where
46 Block: BlockT + 'static,
47 Client: BlockBackend<Block>
48 + HeaderBackend<Block>
49 + ProvideRuntimeApi<Block>
50 + Send
51 + Sync
52 + 'static,
53 Client::Api: Core<Block>,
54{
55 fn block_stats(
56 &self,
57 ext: &Extensions,
58 hash: Block::Hash,
59 ) -> Result<Option<BlockStats>, Error> {
60 check_if_safe(ext)?;
61
62 let block = {
63 let block = self.client.block(hash).map_err(|e| Error::BlockQueryError(Box::new(e)))?;
64 if let Some(block) = block {
65 let (mut header, body) = block.block.deconstruct();
66 header.digest_mut().logs.retain(|item| !matches!(item, DigestItem::Seal(_, _)));
69 Block::new(header, body)
70 } else {
71 return Ok(None);
72 }
73 };
74 let parent_header = {
75 let parent_hash = *block.header().parent_hash();
76 let parent_header = self
77 .client
78 .header(parent_hash)
79 .map_err(|e| Error::BlockQueryError(Box::new(e)))?;
80 if let Some(header) = parent_header {
81 header
82 } else {
83 return Ok(None);
84 }
85 };
86 let block_len = block.encoded_size() as u64;
87 let num_extrinsics = block.extrinsics().len() as u64;
88 let pre_root = *parent_header.state_root();
89 let mut runtime_api = self.client.runtime_api();
90 runtime_api.record_proof();
91 runtime_api
92 .execute_block(parent_header.hash(), block.into())
93 .map_err(|_| Error::BlockExecutionFailed)?;
94 let witness = runtime_api
95 .extract_proof()
96 .expect("We enabled proof recording. A proof must be available; qed");
97 let witness_len = witness.encoded_size() as u64;
98 let witness_compact_len = witness
99 .into_compact_proof::<HasherOf<Block>>(pre_root)
100 .map_err(|_| Error::WitnessCompactionFailed)?
101 .encoded_size() as u64;
102 Ok(Some(BlockStats { witness_len, witness_compact_len, block_len, num_extrinsics }))
103 }
104}