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