Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions bin/node/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub fn create_full<C, P, SC, B>(
) -> Result<jsonrpc_core::IoHandler<sc_rpc_api::Metadata>, Box<dyn std::error::Error + Send + Sync>>
where
C: ProvideRuntimeApi<Block>
+ sc_client_api::BlockBackend<Block>
+ HeaderBackend<Block>
+ AuxStore
+ HeaderMetadata<Block, Error = BlockChainError>
Expand All @@ -123,6 +124,7 @@ where
use pallet_contracts_rpc::{Contracts, ContractsApi};
use pallet_mmr_rpc::{Mmr, MmrApi};
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};
use sc_rpc::dev::{Dev, DevApi};
use substrate_frame_rpc_system::{FullSystem, SystemApi};

let mut io = jsonrpc_core::IoHandler::default();
Expand Down Expand Up @@ -159,19 +161,18 @@ where
subscription_executor,
finality_provider,
)));

io.extend_with(substrate_state_trie_migration_rpc::StateMigrationApi::to_delegate(
substrate_state_trie_migration_rpc::MigrationRpc::new(client.clone(), backend, deny_unsafe),
));

io.extend_with(sc_sync_state_rpc::SyncStateRpcApi::to_delegate(
sc_sync_state_rpc::SyncStateRpcHandler::new(
chain_spec,
client,
client.clone(),
shared_authority_set,
shared_epoch_changes,
)?,
));
io.extend_with(DevApi::to_delegate(Dev::new(client, deny_unsafe)));

Ok(io)
}
1 change: 1 addition & 0 deletions client/rpc-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ log = "0.4.8"
parking_lot = "0.12.0"
thiserror = "1.0"

scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }
sp-core = { version = "6.0.0", path = "../../primitives/core" }
sp-version = { version = "5.0.0", path = "../../primitives/version" }
sp-runtime = { version = "6.0.0", path = "../../primitives/runtime" }
Expand Down
71 changes: 71 additions & 0 deletions client/rpc-api/src/dev/error.rs
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),
}
}
}
64 changes: 64 additions & 0 deletions client/rpc-api/src/dev/mod.rs
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>>;
}
1 change: 1 addition & 0 deletions client/rpc-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub use policy::{DenyUnsafe, UnsafeRpcError};
pub mod author;
pub mod chain;
pub mod child_state;
pub mod dev;
pub mod offchain;
pub mod state;
pub mod system;
118 changes: 118 additions & 0 deletions client/rpc/src/dev/mod.rs
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 }))
}
}
58 changes: 58 additions & 0 deletions client/rpc/src/dev/tests.rs
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(_)));
}
1 change: 1 addition & 0 deletions client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub use sc_rpc_api::{DenyUnsafe, Metadata};

pub mod author;
pub mod chain;
pub mod dev;
pub mod offchain;
pub mod state;
pub mod system;
Expand Down