mod state_full;
mod utils;
#[cfg(test)]
mod tests;
use std::sync::Arc;
use crate::SubscriptionTaskExecutor;
use jsonrpsee::{
core::{async_trait, server::rpc_module::SubscriptionSink, Error as JsonRpseeError, RpcResult},
types::SubscriptionResult,
};
use sc_rpc_api::DenyUnsafe;
use sp_core::{
storage::{PrefixedStorageKey, StorageChangeSet, StorageData, StorageKey},
Bytes,
};
use sp_runtime::traits::Block as BlockT;
use sp_version::RuntimeVersion;
use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi};
use self::error::Error;
use sc_client_api::{
Backend, BlockBackend, BlockchainEvents, ExecutorProvider, ProofProvider, StorageProvider,
};
pub use sc_rpc_api::{child_state::*, state::*};
use sp_blockchain::{HeaderBackend, HeaderMetadata};
const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000;
#[async_trait]
pub trait StateBackend<Block: BlockT, Client>: Send + Sync + 'static
where
Block: BlockT + 'static,
Client: Send + Sync + 'static,
{
fn call(
&self,
block: Option<Block::Hash>,
method: String,
call_data: Bytes,
) -> Result<Bytes, Error>;
fn storage_keys(
&self,
block: Option<Block::Hash>,
prefix: StorageKey,
) -> Result<Vec<StorageKey>, Error>;
fn storage_pairs(
&self,
block: Option<Block::Hash>,
prefix: StorageKey,
) -> Result<Vec<(StorageKey, StorageData)>, Error>;
fn storage_keys_paged(
&self,
block: Option<Block::Hash>,
prefix: Option<StorageKey>,
count: u32,
start_key: Option<StorageKey>,
) -> Result<Vec<StorageKey>, Error>;
fn storage(
&self,
block: Option<Block::Hash>,
key: StorageKey,
) -> Result<Option<StorageData>, Error>;
fn storage_hash(
&self,
block: Option<Block::Hash>,
key: StorageKey,
) -> Result<Option<Block::Hash>, Error>;
async fn storage_size(
&self,
block: Option<Block::Hash>,
key: StorageKey,
deny_unsafe: DenyUnsafe,
) -> Result<Option<u64>, Error>;
fn metadata(&self, block: Option<Block::Hash>) -> Result<Bytes, Error>;
fn runtime_version(&self, block: Option<Block::Hash>) -> Result<RuntimeVersion, Error>;
fn query_storage(
&self,
from: Block::Hash,
to: Option<Block::Hash>,
keys: Vec<StorageKey>,
) -> Result<Vec<StorageChangeSet<Block::Hash>>, Error>;
fn query_storage_at(
&self,
keys: Vec<StorageKey>,
at: Option<Block::Hash>,
) -> Result<Vec<StorageChangeSet<Block::Hash>>, Error>;
fn read_proof(
&self,
block: Option<Block::Hash>,
keys: Vec<StorageKey>,
) -> Result<ReadProof<Block::Hash>, Error>;
fn trace_block(
&self,
block: Block::Hash,
targets: Option<String>,
storage_keys: Option<String>,
methods: Option<String>,
) -> Result<sp_rpc::tracing::TraceBlockResponse, Error>;
fn subscribe_runtime_version(&self, sink: SubscriptionSink);
fn subscribe_storage(&self, sink: SubscriptionSink, keys: Option<Vec<StorageKey>>);
}
pub fn new_full<BE, Block: BlockT, Client>(
client: Arc<Client>,
executor: SubscriptionTaskExecutor,
deny_unsafe: DenyUnsafe,
) -> (State<Block, Client>, ChildState<Block, Client>)
where
Block: BlockT + 'static,
Block::Hash: Unpin,
BE: Backend<Block> + 'static,
Client: ExecutorProvider<Block>
+ StorageProvider<Block, BE>
+ ProofProvider<Block>
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
+ BlockchainEvents<Block>
+ CallApiAt<Block>
+ HeaderBackend<Block>
+ BlockBackend<Block>
+ ProvideRuntimeApi<Block>
+ Send
+ Sync
+ 'static,
Client::Api: Metadata<Block>,
{
let child_backend =
Box::new(self::state_full::FullState::new(client.clone(), executor.clone()));
let backend = Box::new(self::state_full::FullState::new(client, executor));
(State { backend, deny_unsafe }, ChildState { backend: child_backend })
}
pub struct State<Block, Client> {
backend: Box<dyn StateBackend<Block, Client>>,
deny_unsafe: DenyUnsafe,
}
#[async_trait]
impl<Block, Client> StateApiServer<Block::Hash> for State<Block, Client>
where
Block: BlockT + 'static,
Client: Send + Sync + 'static,
{
fn call(&self, method: String, data: Bytes, block: Option<Block::Hash>) -> RpcResult<Bytes> {
self.backend.call(block, method, data).map_err(Into::into)
}
fn storage_keys(
&self,
key_prefix: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Vec<StorageKey>> {
self.backend.storage_keys(block, key_prefix).map_err(Into::into)
}
fn storage_pairs(
&self,
key_prefix: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Vec<(StorageKey, StorageData)>> {
self.deny_unsafe.check_if_safe()?;
self.backend.storage_pairs(block, key_prefix).map_err(Into::into)
}
fn storage_keys_paged(
&self,
prefix: Option<StorageKey>,
count: u32,
start_key: Option<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<Vec<StorageKey>> {
if count > STORAGE_KEYS_PAGED_MAX_COUNT {
return Err(JsonRpseeError::from(Error::InvalidCount {
value: count,
max: STORAGE_KEYS_PAGED_MAX_COUNT,
}))
}
self.backend
.storage_keys_paged(block, prefix, count, start_key)
.map_err(Into::into)
}
fn storage(
&self,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<StorageData>> {
self.backend.storage(block, key).map_err(Into::into)
}
fn storage_hash(
&self,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<Block::Hash>> {
self.backend.storage_hash(block, key).map_err(Into::into)
}
async fn storage_size(
&self,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<u64>> {
self.backend
.storage_size(block, key, self.deny_unsafe)
.await
.map_err(Into::into)
}
fn metadata(&self, block: Option<Block::Hash>) -> RpcResult<Bytes> {
self.backend.metadata(block).map_err(Into::into)
}
fn runtime_version(&self, at: Option<Block::Hash>) -> RpcResult<RuntimeVersion> {
self.backend.runtime_version(at).map_err(Into::into)
}
fn query_storage(
&self,
keys: Vec<StorageKey>,
from: Block::Hash,
to: Option<Block::Hash>,
) -> RpcResult<Vec<StorageChangeSet<Block::Hash>>> {
self.deny_unsafe.check_if_safe()?;
self.backend.query_storage(from, to, keys).map_err(Into::into)
}
fn query_storage_at(
&self,
keys: Vec<StorageKey>,
at: Option<Block::Hash>,
) -> RpcResult<Vec<StorageChangeSet<Block::Hash>>> {
self.backend.query_storage_at(keys, at).map_err(Into::into)
}
fn read_proof(
&self,
keys: Vec<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<ReadProof<Block::Hash>> {
self.backend.read_proof(block, keys).map_err(Into::into)
}
fn trace_block(
&self,
block: Block::Hash,
targets: Option<String>,
storage_keys: Option<String>,
methods: Option<String>,
) -> RpcResult<sp_rpc::tracing::TraceBlockResponse> {
self.deny_unsafe.check_if_safe()?;
self.backend
.trace_block(block, targets, storage_keys, methods)
.map_err(Into::into)
}
fn subscribe_runtime_version(&self, sink: SubscriptionSink) -> SubscriptionResult {
self.backend.subscribe_runtime_version(sink);
Ok(())
}
fn subscribe_storage(
&self,
mut sink: SubscriptionSink,
keys: Option<Vec<StorageKey>>,
) -> SubscriptionResult {
if keys.is_none() {
if let Err(err) = self.deny_unsafe.check_if_safe() {
let _ = sink.reject(JsonRpseeError::from(err));
return Ok(())
}
}
self.backend.subscribe_storage(sink, keys);
Ok(())
}
}
pub trait ChildStateBackend<Block: BlockT, Client>: Send + Sync + 'static
where
Block: BlockT + 'static,
Client: Send + Sync + 'static,
{
fn read_child_proof(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
) -> Result<ReadProof<Block::Hash>, Error>;
fn storage_keys(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
prefix: StorageKey,
) -> Result<Vec<StorageKey>, Error>;
fn storage_keys_paged(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
prefix: Option<StorageKey>,
count: u32,
start_key: Option<StorageKey>,
) -> Result<Vec<StorageKey>, Error>;
fn storage(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
key: StorageKey,
) -> Result<Option<StorageData>, Error>;
fn storage_entries(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
) -> Result<Vec<Option<StorageData>>, Error>;
fn storage_hash(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
key: StorageKey,
) -> Result<Option<Block::Hash>, Error>;
fn storage_size(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
key: StorageKey,
) -> Result<Option<u64>, Error> {
self.storage(block, storage_key, key).map(|x| x.map(|x| x.0.len() as u64))
}
}
pub struct ChildState<Block, Client> {
backend: Box<dyn ChildStateBackend<Block, Client>>,
}
impl<Block, Client> ChildStateApiServer<Block::Hash> for ChildState<Block, Client>
where
Block: BlockT + 'static,
Client: Send + Sync + 'static,
{
fn storage_keys(
&self,
storage_key: PrefixedStorageKey,
key_prefix: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Vec<StorageKey>> {
self.backend.storage_keys(block, storage_key, key_prefix).map_err(Into::into)
}
fn storage_keys_paged(
&self,
storage_key: PrefixedStorageKey,
prefix: Option<StorageKey>,
count: u32,
start_key: Option<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<Vec<StorageKey>> {
self.backend
.storage_keys_paged(block, storage_key, prefix, count, start_key)
.map_err(Into::into)
}
fn storage(
&self,
storage_key: PrefixedStorageKey,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<StorageData>> {
self.backend.storage(block, storage_key, key).map_err(Into::into)
}
fn storage_entries(
&self,
storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<Vec<Option<StorageData>>> {
self.backend.storage_entries(block, storage_key, keys).map_err(Into::into)
}
fn storage_hash(
&self,
storage_key: PrefixedStorageKey,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<Block::Hash>> {
self.backend.storage_hash(block, storage_key, key).map_err(Into::into)
}
fn storage_size(
&self,
storage_key: PrefixedStorageKey,
key: StorageKey,
block: Option<Block::Hash>,
) -> RpcResult<Option<u64>> {
self.backend.storage_size(block, storage_key, key).map_err(Into::into)
}
fn read_child_proof(
&self,
child_storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
block: Option<Block::Hash>,
) -> RpcResult<ReadProof<Block::Hash>> {
self.backend
.read_child_proof(block, child_storage_key, keys)
.map_err(Into::into)
}
}
fn client_err(err: sp_blockchain::Error) -> Error {
Error::Client(Box::new(err))
}