Skip to main content

soil_rpc/dev/
mod.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Implementation of the [`DevApiServer`] trait providing debug utilities for Substrate based
8//! blockchains.
9
10#[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
31/// The Dev API. All methods are unsafe.
32pub struct Dev<Block: BlockT, Client> {
33	client: Arc<Client>,
34	_phantom: PhantomData<Block>,
35}
36
37impl<Block: BlockT, Client> Dev<Block, Client> {
38	/// Create a new Dev API.
39	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				// Remove the `Seal` to ensure we have the number of digests as expected by the
67				// runtime.
68				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}