// 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 . //! Substrate state API. 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::{state::ReadProof, 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; /// State backend API. #[async_trait] pub trait StateBackend: Send + Sync + 'static where Block: BlockT + 'static, Client: Send + Sync + 'static, { /// Call runtime method at given block. fn call( &self, block: Option, method: String, call_data: Bytes, ) -> Result; /// Returns the keys with prefix, leave empty to get all the keys. fn storage_keys( &self, block: Option, prefix: StorageKey, ) -> Result, Error>; /// Returns the keys with prefix along with their values, leave empty to get all the pairs. fn storage_pairs( &self, block: Option, prefix: StorageKey, ) -> Result, Error>; /// Returns the keys with prefix with pagination support. fn storage_keys_paged( &self, block: Option, prefix: Option, count: u32, start_key: Option, ) -> Result, Error>; /// Returns a storage entry at a specific block's state. fn storage( &self, block: Option, key: StorageKey, ) -> Result, Error>; /// Returns the hash of a storage entry at a block's state. fn storage_hash( &self, block: Option, key: StorageKey, ) -> Result, Error>; /// Returns the size of a storage entry at a block's state. /// /// If data is available at `key`, it is returned. Else, the sum of values who's key has `key` /// prefix is returned, i.e. all the storage (double) maps that have this prefix. async fn storage_size( &self, block: Option, key: StorageKey, deny_unsafe: DenyUnsafe, ) -> Result, Error>; /// Returns the runtime metadata as an opaque blob. fn metadata(&self, block: Option) -> Result; /// Get the runtime version. fn runtime_version(&self, block: Option) -> Result; /// Query historical storage entries (by key) starting from a block given as the second /// parameter. /// /// NOTE This first returned result contains the initial state of storage for all keys. /// Subsequent values in the vector represent changes to the previous state (diffs). fn query_storage( &self, from: Block::Hash, to: Option, keys: Vec, ) -> Result>, Error>; /// Query storage entries (by key) starting at block hash given as the second parameter. fn query_storage_at( &self, keys: Vec, at: Option, ) -> Result>, Error>; /// Returns proof of storage entries at a specific block's state. fn read_proof( &self, block: Option, keys: Vec, ) -> Result, Error>; /// Trace storage changes for block fn trace_block( &self, block: Block::Hash, targets: Option, storage_keys: Option, methods: Option, ) -> Result; /// New runtime version subscription fn subscribe_runtime_version(&self, sink: SubscriptionSink); /// New storage subscription fn subscribe_storage(&self, sink: SubscriptionSink, keys: Option>); } /// Create new state API that works on full node. pub fn new_full( client: Arc, executor: SubscriptionTaskExecutor, deny_unsafe: DenyUnsafe, rpc_max_payload: Option, ) -> (State, ChildState) where Block: BlockT + 'static, Block::Hash: Unpin, BE: Backend + 'static, Client: ExecutorProvider + StorageProvider + ProofProvider + HeaderMetadata + BlockchainEvents + CallApiAt + HeaderBackend + BlockBackend + ProvideRuntimeApi + Send + Sync + 'static, Client::Api: Metadata, { let child_backend = Box::new(self::state_full::FullState::new( client.clone(), executor.clone(), rpc_max_payload, )); let backend = Box::new(self::state_full::FullState::new(client, executor, rpc_max_payload)); (State { backend, deny_unsafe }, ChildState { backend: child_backend }) } /// State API with subscriptions support. pub struct State { backend: Box>, /// Whether to deny unsafe calls deny_unsafe: DenyUnsafe, } #[async_trait] impl StateApiServer for State where Block: BlockT + 'static, Client: Send + Sync + 'static, { fn call(&self, method: String, data: Bytes, block: Option) -> RpcResult { self.backend.call(block, method, data).map_err(Into::into) } fn storage_keys( &self, key_prefix: StorageKey, block: Option, ) -> RpcResult> { self.backend.storage_keys(block, key_prefix).map_err(Into::into) } fn storage_pairs( &self, key_prefix: StorageKey, block: Option, ) -> RpcResult> { self.deny_unsafe.check_if_safe()?; self.backend.storage_pairs(block, key_prefix).map_err(Into::into) } fn storage_keys_paged( &self, prefix: Option, count: u32, start_key: Option, block: Option, ) -> RpcResult> { 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, ) -> RpcResult> { self.backend.storage(block, key).map_err(Into::into) } fn storage_hash( &self, key: StorageKey, block: Option, ) -> RpcResult> { self.backend.storage_hash(block, key).map_err(Into::into) } async fn storage_size( &self, key: StorageKey, block: Option, ) -> RpcResult> { self.backend .storage_size(block, key, self.deny_unsafe) .await .map_err(Into::into) } fn metadata(&self, block: Option) -> RpcResult { self.backend.metadata(block).map_err(Into::into) } fn runtime_version(&self, at: Option) -> RpcResult { self.backend.runtime_version(at).map_err(Into::into) } fn query_storage( &self, keys: Vec, from: Block::Hash, to: Option, ) -> RpcResult>> { self.deny_unsafe.check_if_safe()?; self.backend.query_storage(from, to, keys).map_err(Into::into) } fn query_storage_at( &self, keys: Vec, at: Option, ) -> RpcResult>> { self.backend.query_storage_at(keys, at).map_err(Into::into) } fn read_proof( &self, keys: Vec, block: Option, ) -> RpcResult> { self.backend.read_proof(block, keys).map_err(Into::into) } /// Re-execute the given block with the tracing targets given in `targets` /// and capture all state changes. /// /// Note: requires the node to run with `--rpc-methods=Unsafe`. /// Note: requires runtimes compiled with wasm tracing support, `--features with-tracing`. fn trace_block( &self, block: Block::Hash, targets: Option, storage_keys: Option, methods: Option, ) -> RpcResult { 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>, ) -> 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(()) } } /// Child state backend API. pub trait ChildStateBackend: Send + Sync + 'static where Block: BlockT + 'static, Client: Send + Sync + 'static, { /// Returns proof of storage for a child key entries at a specific block's state. fn read_child_proof( &self, block: Option, storage_key: PrefixedStorageKey, keys: Vec, ) -> Result, Error>; /// Returns the keys with prefix from a child storage, /// leave prefix empty to get all the keys. fn storage_keys( &self, block: Option, storage_key: PrefixedStorageKey, prefix: StorageKey, ) -> Result, Error>; /// Returns the keys with prefix from a child storage with pagination support. fn storage_keys_paged( &self, block: Option, storage_key: PrefixedStorageKey, prefix: Option, count: u32, start_key: Option, ) -> Result, Error>; /// Returns a child storage entry at a specific block's state. fn storage( &self, block: Option, storage_key: PrefixedStorageKey, key: StorageKey, ) -> Result, Error>; /// Returns child storage entries at a specific block's state. fn storage_entries( &self, block: Option, storage_key: PrefixedStorageKey, keys: Vec, ) -> Result>, Error>; /// Returns the hash of a child storage entry at a block's state. fn storage_hash( &self, block: Option, storage_key: PrefixedStorageKey, key: StorageKey, ) -> Result, Error>; /// Returns the size of a child storage entry at a block's state. fn storage_size( &self, block: Option, storage_key: PrefixedStorageKey, key: StorageKey, ) -> Result, Error> { self.storage(block, storage_key, key).map(|x| x.map(|x| x.0.len() as u64)) } } /// Child state API with subscriptions support. pub struct ChildState { backend: Box>, } impl ChildStateApiServer for ChildState where Block: BlockT + 'static, Client: Send + Sync + 'static, { fn storage_keys( &self, storage_key: PrefixedStorageKey, key_prefix: StorageKey, block: Option, ) -> RpcResult> { self.backend.storage_keys(block, storage_key, key_prefix).map_err(Into::into) } fn storage_keys_paged( &self, storage_key: PrefixedStorageKey, prefix: Option, count: u32, start_key: Option, block: Option, ) -> RpcResult> { 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, ) -> RpcResult> { self.backend.storage(block, storage_key, key).map_err(Into::into) } fn storage_entries( &self, storage_key: PrefixedStorageKey, keys: Vec, block: Option, ) -> RpcResult>> { self.backend.storage_entries(block, storage_key, keys).map_err(Into::into) } fn storage_hash( &self, storage_key: PrefixedStorageKey, key: StorageKey, block: Option, ) -> RpcResult> { self.backend.storage_hash(block, storage_key, key).map_err(Into::into) } fn storage_size( &self, storage_key: PrefixedStorageKey, key: StorageKey, block: Option, ) -> RpcResult> { self.backend.storage_size(block, storage_key, key).map_err(Into::into) } fn read_child_proof( &self, child_storage_key: PrefixedStorageKey, keys: Vec, block: Option, ) -> RpcResult> { 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)) }