// This file is part of Substrate. // Copyright (C) 2017-2020 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 state_light; #[cfg(test)] mod tests; use std::sync::Arc; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId, manager::SubscriptionManager}; use rpc::{Result as RpcResult, futures::{Future, future::result}}; use sc_rpc_api::state::ReadProof; use sc_client_api::light::{RemoteBlockchain, Fetcher}; use sp_core::{Bytes, storage::{StorageKey, PrefixedStorageKey, StorageData, StorageChangeSet}}; use sp_version::RuntimeVersion; use sp_runtime::traits::Block as BlockT; use sp_api::{Metadata, ProvideRuntimeApi, CallApiAt}; use self::error::{Error, FutureResult}; pub use sc_rpc_api::state::*; pub use sc_rpc_api::child_state::*; use sc_client_api::{ExecutorProvider, StorageProvider, BlockchainEvents, Backend, ProofProvider}; use sp_blockchain::{HeaderMetadata, HeaderBackend}; const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000; /// State backend API. 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, ) -> FutureResult; /// Returns the keys with prefix, leave empty to get all the keys. fn storage_keys( &self, block: Option, prefix: StorageKey, ) -> FutureResult>; /// Returns the keys with prefix along with their values, leave empty to get all the pairs. fn storage_pairs( &self, block: Option, prefix: StorageKey, ) -> FutureResult>; /// Returns the keys with prefix with pagination support. fn storage_keys_paged( &self, block: Option, prefix: Option, count: u32, start_key: Option, ) -> FutureResult>; /// Returns a storage entry at a specific block's state. fn storage( &self, block: Option, key: StorageKey, ) -> FutureResult>; /// Returns the hash of a storage entry at a block's state. fn storage_hash( &self, block: Option, key: StorageKey, ) -> FutureResult>; /// 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. fn storage_size( &self, block: Option, key: StorageKey, ) -> FutureResult>; /// Returns the runtime metadata as an opaque blob. fn metadata(&self, block: Option) -> FutureResult; /// Get the runtime version. fn runtime_version(&self, block: Option) -> FutureResult; /// 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, ) -> FutureResult>>; /// Query storage entries (by key) starting at block hash given as the second parameter. fn query_storage_at( &self, keys: Vec, at: Option ) -> FutureResult>>; /// Returns proof of storage entries at a specific block's state. fn read_proof( &self, block: Option, keys: Vec, ) -> FutureResult>; /// New runtime version subscription fn subscribe_runtime_version( &self, _meta: crate::Metadata, subscriber: Subscriber, ); /// Unsubscribe from runtime version subscription fn unsubscribe_runtime_version( &self, _meta: Option, id: SubscriptionId, ) -> RpcResult; /// New storage subscription fn subscribe_storage( &self, _meta: crate::Metadata, subscriber: Subscriber>, keys: Option>, ); /// Unsubscribe from storage subscription fn unsubscribe_storage( &self, _meta: Option, id: SubscriptionId, ) -> RpcResult; } /// Create new state API that works on full node. pub fn new_full( client: Arc, subscriptions: SubscriptionManager, ) -> (State, ChildState) where Block: BlockT + 'static, BE: Backend + 'static, Client: ExecutorProvider + StorageProvider + ProofProvider + HeaderBackend + HeaderMetadata + BlockchainEvents + CallApiAt + ProvideRuntimeApi + Send + Sync + 'static, Client::Api: Metadata, { let child_backend = Box::new( self::state_full::FullState::new(client.clone(), subscriptions.clone()) ); let backend = Box::new(self::state_full::FullState::new(client, subscriptions)); (State { backend }, ChildState { backend: child_backend }) } /// Create new state API that works on light node. pub fn new_light>( client: Arc, subscriptions: SubscriptionManager, remote_blockchain: Arc>, fetcher: Arc, ) -> (State, ChildState) where Block: BlockT + 'static, BE: Backend + 'static, Client: ExecutorProvider + StorageProvider + HeaderMetadata + ProvideRuntimeApi + HeaderBackend + BlockchainEvents + Send + Sync + 'static, F: Send + Sync + 'static, { let child_backend = Box::new(self::state_light::LightState::new( client.clone(), subscriptions.clone(), remote_blockchain.clone(), fetcher.clone(), )); let backend = Box::new(self::state_light::LightState::new( client, subscriptions, remote_blockchain, fetcher, )); (State { backend }, ChildState { backend: child_backend }) } /// State API with subscriptions support. pub struct State { backend: Box>, } impl StateApi for State where Block: BlockT + 'static, Client: Send + Sync + 'static, { type Metadata = crate::Metadata; fn call(&self, method: String, data: Bytes, block: Option) -> FutureResult { self.backend.call(block, method, data) } fn storage_keys( &self, key_prefix: StorageKey, block: Option, ) -> FutureResult> { self.backend.storage_keys(block, key_prefix) } fn storage_pairs( &self, key_prefix: StorageKey, block: Option, ) -> FutureResult> { self.backend.storage_pairs(block, key_prefix) } fn storage_keys_paged( &self, prefix: Option, count: u32, start_key: Option, block: Option, ) -> FutureResult> { if count > STORAGE_KEYS_PAGED_MAX_COUNT { return Box::new(result(Err( Error::InvalidCount { value: count, max: STORAGE_KEYS_PAGED_MAX_COUNT, } ))); } self.backend.storage_keys_paged(block, prefix, count, start_key) } fn storage(&self, key: StorageKey, block: Option) -> FutureResult> { self.backend.storage(block, key) } fn storage_hash(&self, key: StorageKey, block: Option) -> FutureResult> { self.backend.storage_hash(block, key) } fn storage_size(&self, key: StorageKey, block: Option) -> FutureResult> { self.backend.storage_size(block, key) } fn metadata(&self, block: Option) -> FutureResult { self.backend.metadata(block) } fn query_storage( &self, keys: Vec, from: Block::Hash, to: Option ) -> FutureResult>> { self.backend.query_storage(from, to, keys) } fn query_storage_at( &self, keys: Vec, at: Option ) -> FutureResult>> { self.backend.query_storage_at(keys, at) } fn read_proof(&self, keys: Vec, block: Option) -> FutureResult> { self.backend.read_proof(block, keys) } fn subscribe_storage( &self, meta: Self::Metadata, subscriber: Subscriber>, keys: Option> ) { self.backend.subscribe_storage(meta, subscriber, keys); } fn unsubscribe_storage(&self, meta: Option, id: SubscriptionId) -> RpcResult { self.backend.unsubscribe_storage(meta, id) } fn runtime_version(&self, at: Option) -> FutureResult { self.backend.runtime_version(at) } fn subscribe_runtime_version(&self, meta: Self::Metadata, subscriber: Subscriber) { self.backend.subscribe_runtime_version(meta, subscriber); } fn unsubscribe_runtime_version( &self, meta: Option, id: SubscriptionId, ) -> RpcResult { self.backend.unsubscribe_runtime_version(meta, id) } } /// Child state backend API. pub trait ChildStateBackend: Send + Sync + 'static where Block: BlockT + 'static, Client: Send + Sync + 'static, { /// 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, ) -> FutureResult>; /// Returns a child storage entry at a specific block's state. fn storage( &self, block: Option, storage_key: PrefixedStorageKey, key: StorageKey, ) -> FutureResult>; /// Returns the hash of a child storage entry at a block's state. fn storage_hash( &self, block: Option, storage_key: PrefixedStorageKey, key: StorageKey, ) -> FutureResult>; /// Returns the size of a child storage entry at a block's state. fn storage_size( &self, block: Option, storage_key: PrefixedStorageKey, key: StorageKey, ) -> FutureResult> { Box::new(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 ChildStateApi for ChildState where Block: BlockT + 'static, Client: Send + Sync + 'static, { type Metadata = crate::Metadata; fn storage( &self, storage_key: PrefixedStorageKey, key: StorageKey, block: Option ) -> FutureResult> { self.backend.storage(block, storage_key, key) } fn storage_keys( &self, storage_key: PrefixedStorageKey, key_prefix: StorageKey, block: Option ) -> FutureResult> { self.backend.storage_keys(block, storage_key, key_prefix) } fn storage_hash( &self, storage_key: PrefixedStorageKey, key: StorageKey, block: Option ) -> FutureResult> { self.backend.storage_hash(block, storage_key, key) } fn storage_size( &self, storage_key: PrefixedStorageKey, key: StorageKey, block: Option ) -> FutureResult> { self.backend.storage_size(block, storage_key, key) } } fn client_err(err: sp_blockchain::Error) -> Error { Error::Client(Box::new(err)) }