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