This repository was archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add dev_getBlockStats RPC
#10939
Merged
Merged
Add dev_getBlockStats RPC
#10939
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
fd2c21a
Add chain_getBlockStats rpc
athei 2bc69ec
Fix broken doc link
athei 5525c83
Merge branch 'master' into at-stat-rpc
athei 7125c06
Merge branch 'master' into at-stat-rpc
ascjones 6189c15
Apply suggestions from code review
athei e15475f
Apply suggestions from code review
athei 322f76e
fmt
athei b59bfbf
Merge branch 'master' into at-stat-rpc
athei 2df2490
Fix compilation
athei ae0e114
Move Blockstats
athei 66bda0e
Apply suggestions from code review
athei 6d81a8d
fmt
athei File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2022 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| //! Error helpers for Dev RPC module. | ||
|
|
||
| use crate::errors; | ||
| use jsonrpc_core as rpc; | ||
|
|
||
| /// Dev RPC Result type. | ||
| pub type Result<T> = std::result::Result<T, Error>; | ||
|
|
||
| /// Dev RPC future Result type. | ||
| pub type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T>>; | ||
|
|
||
| /// Dev RPC errors. | ||
| #[derive(Debug, thiserror::Error)] | ||
| pub enum Error { | ||
| /// Failed to query specified block or its parent: Probably an invalid hash. | ||
| #[error("Error while querying block: {0}")] | ||
| BlockQueryError(Box<dyn std::error::Error + Send>), | ||
| /// The re-execution of the specified block failed. | ||
| #[error("Failed to re-execute the specified block")] | ||
| BlockExecutionFailed, | ||
| /// The witness compaction failed. | ||
| #[error("Failed to create to compact the witness")] | ||
| WitnessCompactionFailed, | ||
| /// The method is marked as unsafe but unsafe flag wasn't supplied on the CLI. | ||
| #[error(transparent)] | ||
| UnsafeRpcCalled(#[from] crate::policy::UnsafeRpcError), | ||
| } | ||
|
|
||
| /// Base error code for all dev errors. | ||
| const BASE_ERROR: i64 = 6000; | ||
|
|
||
| impl From<Error> for rpc::Error { | ||
| fn from(e: Error) -> Self { | ||
| match e { | ||
| Error::BlockQueryError(_) => rpc::Error { | ||
| code: rpc::ErrorCode::ServerError(BASE_ERROR + 1), | ||
| message: e.to_string(), | ||
| data: None, | ||
| }, | ||
| Error::BlockExecutionFailed => rpc::Error { | ||
| code: rpc::ErrorCode::ServerError(BASE_ERROR + 3), | ||
| message: e.to_string(), | ||
| data: None, | ||
| }, | ||
| Error::WitnessCompactionFailed => rpc::Error { | ||
| code: rpc::ErrorCode::ServerError(BASE_ERROR + 4), | ||
| message: e.to_string(), | ||
| data: None, | ||
| }, | ||
| e => errors::internal(e), | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2022 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| //! Substrate dev API containing RPCs that are mainly meant for debugging and stats collection for | ||
| //! developers. The endpoints in this RPC module are not meant to be available to non-local users | ||
| //! and are all marked `unsafe`. | ||
|
|
||
| pub mod error; | ||
|
|
||
| use self::error::Result; | ||
| use codec::{Decode, Encode}; | ||
| use jsonrpc_derive::rpc; | ||
| use scale_info::TypeInfo; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| /// Statistics of a block returned by the `dev_getBlockStats` RPC. | ||
| #[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, Serialize, Deserialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct BlockStats { | ||
| /// The length in bytes of the storage proof produced by executing the block. | ||
| pub witness_len: u64, | ||
| /// The length in bytes of the storage proof after compaction. | ||
| pub witness_compact_len: u64, | ||
| /// Length of the block in bytes. | ||
| /// | ||
| /// This information can also be acquired by downloading the whole block. This merely | ||
| /// saves some complexity on the client side. | ||
| pub block_len: u64, | ||
| /// Number of extrinsics in the block. | ||
| /// | ||
| /// This information can also be acquired by downloading the whole block. This merely | ||
| /// saves some complexity on the client side. | ||
| pub num_extrinsics: u64, | ||
| } | ||
|
|
||
| /// Substrate dev API. | ||
| /// | ||
| /// This API contains unstable and unsafe methods only meant for development nodes. They | ||
| /// are all flagged as unsafe for this reason. | ||
| #[rpc] | ||
| pub trait DevApi<Hash> { | ||
| /// Reexecute the specified `block_hash` and gather statistics while doing so. | ||
| /// | ||
| /// This function requires the specified block and its parent to be available | ||
| /// at the queried node. If either the specified block or the parent is pruned, | ||
| /// this function will return `None`. | ||
| #[rpc(name = "dev_getBlockStats")] | ||
| fn block_stats(&self, block_hash: Hash) -> Result<Option<BlockStats>>; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| //! Implementation of the [`DevApi`] trait providing debug utilities for Substrate based | ||
| //! blockchains. | ||
|
|
||
| #[cfg(test)] | ||
| mod tests; | ||
|
|
||
| pub use sc_rpc_api::dev::{BlockStats, DevApi}; | ||
|
|
||
| use sc_client_api::{BlockBackend, HeaderBackend}; | ||
| use sc_rpc_api::{ | ||
| dev::error::{Error, Result}, | ||
| DenyUnsafe, | ||
| }; | ||
| use sp_api::{ApiExt, Core, ProvideRuntimeApi}; | ||
| use sp_core::Encode; | ||
| use sp_runtime::{ | ||
| generic::{BlockId, DigestItem}, | ||
| traits::{Block as BlockT, Header}, | ||
| }; | ||
| use std::{ | ||
| marker::{PhantomData, Send, Sync}, | ||
| sync::Arc, | ||
| }; | ||
|
|
||
| type HasherOf<Block> = <<Block as BlockT>::Header as Header>::Hashing; | ||
|
|
||
| /// The Dev API. All methods are unsafe. | ||
| pub struct Dev<Block: BlockT, Client> { | ||
| client: Arc<Client>, | ||
| deny_unsafe: DenyUnsafe, | ||
| _phantom: PhantomData<Block>, | ||
| } | ||
|
|
||
| impl<Block: BlockT, Client> Dev<Block, Client> { | ||
| /// Create a new Dev API. | ||
| pub fn new(client: Arc<Client>, deny_unsafe: DenyUnsafe) -> Self { | ||
| Self { client, deny_unsafe, _phantom: PhantomData::default() } | ||
| } | ||
| } | ||
|
|
||
| impl<Block, Client> DevApi<Block::Hash> for Dev<Block, Client> | ||
| where | ||
| Block: BlockT + 'static, | ||
| Client: BlockBackend<Block> | ||
| + HeaderBackend<Block> | ||
| + ProvideRuntimeApi<Block> | ||
| + Send | ||
| + Sync | ||
| + 'static, | ||
| Client::Api: Core<Block>, | ||
| { | ||
| fn block_stats(&self, hash: Block::Hash) -> Result<Option<BlockStats>> { | ||
| self.deny_unsafe.check_if_safe()?; | ||
|
|
||
| let block = { | ||
| let block = self | ||
| .client | ||
| .block(&BlockId::Hash(hash)) | ||
| .map_err(|e| Error::BlockQueryError(Box::new(e)))?; | ||
| if let Some(block) = block { | ||
| let (mut header, body) = block.block.deconstruct(); | ||
| // Remove the `Seal` to ensure we have the number of digests as expected by the | ||
| // runtime. | ||
| header.digest_mut().logs.retain(|item| !matches!(item, DigestItem::Seal(_, _))); | ||
| Block::new(header, body) | ||
| } else { | ||
| return Ok(None) | ||
| } | ||
| }; | ||
| let parent_header = { | ||
| let parent_hash = *block.header().parent_hash(); | ||
| let parent_header = self | ||
| .client | ||
| .header(BlockId::Hash(parent_hash)) | ||
| .map_err(|e| Error::BlockQueryError(Box::new(e)))?; | ||
| if let Some(header) = parent_header { | ||
| header | ||
| } else { | ||
| return Ok(None) | ||
| } | ||
| }; | ||
| let block_len = block.encoded_size() as u64; | ||
| let num_extrinsics = block.extrinsics().len() as u64; | ||
| let pre_root = *parent_header.state_root(); | ||
| let mut runtime_api = self.client.runtime_api(); | ||
| runtime_api.record_proof(); | ||
| runtime_api | ||
| .execute_block(&BlockId::Hash(parent_header.hash()), block) | ||
| .map_err(|_| Error::BlockExecutionFailed)?; | ||
| let witness = runtime_api | ||
| .extract_proof() | ||
| .expect("We enabled proof recording. A proof must be available; qed"); | ||
| let witness_len = witness.encoded_size() as u64; | ||
| let witness_compact_len = witness | ||
| .into_compact_proof::<HasherOf<Block>>(pre_root) | ||
| .map_err(|_| Error::WitnessCompactionFailed)? | ||
| .encoded_size() as u64; | ||
| Ok(Some(BlockStats { witness_len, witness_compact_len, block_len, num_extrinsics })) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| use super::*; | ||
| use assert_matches::assert_matches; | ||
| use futures::executor; | ||
| use sc_block_builder::BlockBuilderProvider; | ||
| use sp_blockchain::HeaderBackend; | ||
| use sp_consensus::BlockOrigin; | ||
| use substrate_test_runtime_client::{prelude::*, runtime::Block}; | ||
|
|
||
| #[test] | ||
| fn block_stats_work() { | ||
| let mut client = Arc::new(substrate_test_runtime_client::new()); | ||
| let api = <Dev<Block, _>>::new(client.clone(), DenyUnsafe::No); | ||
|
|
||
| let block = client.new_block(Default::default()).unwrap().build().unwrap().block; | ||
| executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); | ||
|
|
||
| // Can't gather stats for a block without a parent. | ||
| assert_eq!(api.block_stats(client.genesis_hash()).unwrap(), None); | ||
|
|
||
| assert_eq!( | ||
| api.block_stats(client.info().best_hash).unwrap(), | ||
| Some(BlockStats { | ||
| witness_len: 597, | ||
| witness_compact_len: 500, | ||
| block_len: 99, | ||
| num_extrinsics: 0, | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn deny_unsafe_works() { | ||
| let mut client = Arc::new(substrate_test_runtime_client::new()); | ||
| let api = <Dev<Block, _>>::new(client.clone(), DenyUnsafe::Yes); | ||
|
|
||
| let block = client.new_block(Default::default()).unwrap().build().unwrap().block; | ||
| executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); | ||
|
|
||
| assert_matches!(api.block_stats(client.info().best_hash), Err(Error::UnsafeRpcCalled(_))); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.