From 52d0173ec377b488cf383d966a6448d2f27d4984 Mon Sep 17 00:00:00 2001 From: Yuanchao Sun <yuanchao.sun@gmail.com> Date: Tue, 21 Apr 2020 03:09:55 +0800 Subject: [PATCH] Add RPC function state_getProof, resolves #1110 (#5649) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add RPC function state_getProof, resolves #1110 * Apply suggestions from code review * Update client/rpc/src/state/state_full.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update Cargo.lock * Make block hash optional * Wrap StorageProof as Bytes * Add struct ReadProof * Fix typo Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> --- substrate/client/rpc-api/src/state/helpers.rs | 30 +++++++++++++++++++ substrate/client/rpc-api/src/state/mod.rs | 6 ++++ substrate/client/rpc/src/state/mod.rs | 17 +++++++++-- substrate/client/rpc/src/state/state_full.rs | 26 ++++++++++++++-- substrate/client/rpc/src/state/state_light.rs | 10 ++++++- 5 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 substrate/client/rpc-api/src/state/helpers.rs diff --git a/substrate/client/rpc-api/src/state/helpers.rs b/substrate/client/rpc-api/src/state/helpers.rs new file mode 100644 index 00000000000..516b6c80c49 --- /dev/null +++ b/substrate/client/rpc-api/src/state/helpers.rs @@ -0,0 +1,30 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Substrate state API helpers. + +use sp_core::Bytes; +use serde::{Serialize, Deserialize}; + +/// ReadProof struct returned by the RPC +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ReadProof<Hash> { + /// Block hash used to generate the proof + pub at: Hash, + /// A proof used to prove that storage entries are included in the storage trie + pub proof: Vec<Bytes>, +} diff --git a/substrate/client/rpc-api/src/state/mod.rs b/substrate/client/rpc-api/src/state/mod.rs index 243a33c18d4..3d38a16eb4a 100644 --- a/substrate/client/rpc-api/src/state/mod.rs +++ b/substrate/client/rpc-api/src/state/mod.rs @@ -17,6 +17,7 @@ //! Substrate state API. pub mod error; +pub mod helpers; use jsonrpc_core::Result as RpcResult; use jsonrpc_core::futures::Future; @@ -28,6 +29,7 @@ use sp_version::RuntimeVersion; use self::error::FutureResult; pub use self::gen_client::Client as StateClient; +pub use self::helpers::ReadProof; /// Substrate state API #[rpc] @@ -100,6 +102,10 @@ pub trait StateApi<Hash> { at: Option<Hash>, ) -> FutureResult<Vec<StorageChangeSet<Hash>>>; + /// Returns proof of storage entries at a specific block's state. + #[rpc(name = "state_getReadProof")] + fn read_proof(&self, keys: Vec<StorageKey>, hash: Option<Hash>) -> FutureResult<ReadProof<Hash>>; + /// New runtime version subscription #[pubsub( subscription = "state_runtimeVersion", diff --git a/substrate/client/rpc/src/state/mod.rs b/substrate/client/rpc/src/state/mod.rs index e6962824333..b3ac3674225 100644 --- a/substrate/client/rpc/src/state/mod.rs +++ b/substrate/client/rpc/src/state/mod.rs @@ -26,7 +26,7 @@ use std::sync::Arc; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use rpc::{Result as RpcResult, futures::{Future, future::result}}; -use sc_rpc_api::Subscriptions; +use sc_rpc_api::{Subscriptions, state::ReadProof}; use sc_client::{light::{blockchain::RemoteBlockchain, fetcher::Fetcher}}; use sp_core::{Bytes, storage::{StorageKey, PrefixedStorageKey, StorageData, StorageChangeSet}}; use sp_version::RuntimeVersion; @@ -38,7 +38,7 @@ 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}; +use sc_client_api::{ExecutorProvider, StorageProvider, BlockchainEvents, Backend, ProofProvider}; use sp_blockchain::{HeaderMetadata, HeaderBackend}; const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000; @@ -128,6 +128,13 @@ pub trait StateBackend<Block: BlockT, Client>: Send + Sync + 'static at: Option<Block::Hash> ) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>>; + /// Returns proof of storage entries at a specific block's state. + fn read_proof( + &self, + block: Option<Block::Hash>, + keys: Vec<StorageKey>, + ) -> FutureResult<ReadProof<Block::Hash>>; + /// New runtime version subscription fn subscribe_runtime_version( &self, @@ -166,7 +173,7 @@ pub fn new_full<BE, Block: BlockT, Client>( where Block: BlockT + 'static, BE: Backend<Block> + 'static, - Client: ExecutorProvider<Block> + StorageProvider<Block, BE> + HeaderBackend<Block> + Client: ExecutorProvider<Block> + StorageProvider<Block, BE> + ProofProvider<Block> + HeaderBackend<Block> + HeaderMetadata<Block, Error = sp_blockchain::Error> + BlockchainEvents<Block> + CallApiAt<Block, Error = sp_blockchain::Error> + ProvideRuntimeApi<Block> + Send + Sync + 'static, @@ -294,6 +301,10 @@ impl<Block, Client> StateApi<Block::Hash> for State<Block, Client> self.backend.query_storage_at(keys, at) } + fn read_proof(&self, keys: Vec<StorageKey>, block: Option<Block::Hash>) -> FutureResult<ReadProof<Block::Hash>> { + self.backend.read_proof(block, keys) + } + fn subscribe_storage( &self, meta: Self::Metadata, diff --git a/substrate/client/rpc/src/state/state_full.rs b/substrate/client/rpc/src/state/state_full.rs index a9767c34fcf..4546692b7bd 100644 --- a/substrate/client/rpc/src/state/state_full.rs +++ b/substrate/client/rpc/src/state/state_full.rs @@ -24,7 +24,7 @@ use log::warn; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use rpc::{Result as RpcResult, futures::{stream, Future, Sink, Stream, future::result}}; -use sc_rpc_api::Subscriptions; +use sc_rpc_api::{Subscriptions, state::ReadProof}; use sc_client_api::backend::Backend; use sp_blockchain::{Result as ClientResult, Error as ClientError, HeaderMetadata, CachedHeaderMetadata, HeaderBackend}; use sc_client::BlockchainEvents; @@ -41,7 +41,7 @@ use sp_api::{Metadata, ProvideRuntimeApi, CallApiAt}; use super::{StateBackend, ChildStateBackend, error::{FutureResult, Error, Result}, client_err}; use std::marker::PhantomData; -use sc_client_api::{CallExecutor, StorageProvider, ExecutorProvider}; +use sc_client_api::{CallExecutor, StorageProvider, ExecutorProvider, ProofProvider}; /// Ranges to query in state_queryStorage. struct QueryStorageRange<Block: BlockT> { @@ -219,7 +219,7 @@ impl<BE, Block: BlockT, Client> FullState<BE, Block, Client> impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Client> where Block: BlockT + 'static, BE: Backend<Block> + 'static, - Client: ExecutorProvider<Block> + StorageProvider<Block, BE> + HeaderBackend<Block> + Client: ExecutorProvider<Block> + StorageProvider<Block, BE> + ProofProvider<Block> + HeaderBackend<Block> + HeaderMetadata<Block, Error = sp_blockchain::Error> + BlockchainEvents<Block> + CallApiAt<Block, Error = sp_blockchain::Error> + ProvideRuntimeApi<Block> + Send + Sync + 'static, @@ -351,6 +351,26 @@ impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Cli self.query_storage(at, Some(at), keys) } + fn read_proof( + &self, + block: Option<Block::Hash>, + keys: Vec<StorageKey>, + ) -> FutureResult<ReadProof<Block::Hash>> { + Box::new(result( + self.block_or_best(block) + .and_then(|block| { + self.client + .read_proof( + &BlockId::Hash(block), + &mut keys.iter().map(|key| key.0.as_ref()), + ) + .map(|proof| proof.iter_nodes().map(|node| node.into()).collect()) + .map(|proof| ReadProof { at: block, proof }) + }) + .map_err(client_err), + )) + } + fn subscribe_runtime_version( &self, _meta: crate::metadata::Metadata, diff --git a/substrate/client/rpc/src/state/state_light.rs b/substrate/client/rpc/src/state/state_light.rs index 27adbcd6919..10adab9cc31 100644 --- a/substrate/client/rpc/src/state/state_light.rs +++ b/substrate/client/rpc/src/state/state_light.rs @@ -38,7 +38,7 @@ use rpc::{ futures::stream::Stream, }; -use sc_rpc_api::Subscriptions; +use sc_rpc_api::{Subscriptions, state::ReadProof}; use sp_blockchain::{Error as ClientError, HeaderBackend}; use sc_client::{ BlockchainEvents, @@ -279,6 +279,14 @@ impl<Block, F, Client> StateBackend<Block, Client> for LightState<Block, F, Clie Box::new(result(Err(client_err(ClientError::NotAvailableOnLightClient)))) } + fn read_proof( + &self, + _block: Option<Block::Hash>, + _keys: Vec<StorageKey>, + ) -> FutureResult<ReadProof<Block::Hash>> { + Box::new(result(Err(client_err(ClientError::NotAvailableOnLightClient)))) + } + fn subscribe_storage( &self, _meta: crate::metadata::Metadata, -- GitLab