From 951d911a60c3c126b75da8fb5152c42abd677ad2 Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Wed, 6 Nov 2019 17:43:17 -0800 Subject: [PATCH 1/6] Create typed client helpers for querying chain state storage items declared by 'decl_storage!'. --- Cargo.lock | 18 ++ Cargo.toml | 2 +- core/rpc/custom/Cargo.toml | 22 +++ core/rpc/custom/src/lib.rs | 316 ++++++++++++++++++++++++++++++ core/test-runtime/src/.#system.rs | 1 + core/test-runtime/src/system.rs | 2 +- 6 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 core/rpc/custom/Cargo.toml create mode 100644 core/rpc/custom/src/lib.rs create mode 120000 core/test-runtime/src/.#system.rs diff --git a/Cargo.lock b/Cargo.lock index 8c215ddd12611..676ed2e9fafe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1794,6 +1794,7 @@ dependencies = [ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-pubsub 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5913,6 +5914,23 @@ dependencies = [ "substrate-transaction-graph 2.0.0", ] +[[package]] +name = "substrate-rpc-custom" +version = "0.1.0" +dependencies = [ + "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-transports 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-primitives-storage 2.0.0", + "substrate-rpc-api 2.0.0", + "substrate-test-runtime 2.0.0", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "substrate-rpc-primitives" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index bdc0d8737518c..4cb358ef55907 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ members = [ "core/panic-handler", "core/primitives", "core/rpc", + "core/rpc/custom", "core/rpc/primitives", "core/rpc-servers", "core/serializer", @@ -109,4 +110,3 @@ members = [ [profile.release] # Substrate runtime requires unwinding. panic = "unwind" - diff --git a/core/rpc/custom/Cargo.toml b/core/rpc/custom/Cargo.toml new file mode 100644 index 0000000000000..46bcf640be1a4 --- /dev/null +++ b/core/rpc/custom/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "substrate-rpc-custom" +version = "0.1.0" +authors = ["Andrew Dirksen "] +edition = "2018" + +[dependencies] +srml-support = { path = "../../../srml/support" } +substrate-rpc-api = { path = "../../../core/rpc/api" } +substrate-primitives-storage = { path = "../../../core/primitives/storage" } +jsonrpc-client-transports = "14" +jsonrpc-core = "14" +parity-scale-codec = "1" +futures-preview = { version = "0.3.0-alpha.19", features = ["compat"] } +serde = "1" + +[dev-dependencies] +substrate-test-runtime = { path = "../../../core/test-runtime" } +srml-system = { path = "../../../srml/system" } +srml-support = { path = "../../../srml/support" } +jsonrpc-client-transports = { version = "14", features = ["http"] } +tokio = "0.1" diff --git a/core/rpc/custom/src/lib.rs b/core/rpc/custom/src/lib.rs new file mode 100644 index 0000000000000..b51f09a69862d --- /dev/null +++ b/core/rpc/custom/src/lib.rs @@ -0,0 +1,316 @@ +//! Combines [substrate_rpc_api::state::StateClient] with [srml_support::storage::generator] traits +//! to provide strongly typed chain state queries over rpc. + +use jsonrpc_client_transports::RpcError; +use jsonrpc_core::futures::Future; +use parity_scale_codec::{DecodeAll, FullCodec}; +use serde::{de::DeserializeOwned, Serialize}; +use srml_support::storage::generator::{ + StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue, +}; +use substrate_primitives_storage::{StorageData, StorageKey}; +use substrate_rpc_api::state::StateClient; + +/// Queries a typed global from chain state. +/// +/// # Arguments +/// +/// `state_client` is a json rpc client generated by in the [substrate_rpc_api::state] module +/// using [jsonrpc_derive::rpc]. +/// +/// `block_index` specifies the block for which storage is to be queried. If None, the latest block +/// is queried. +/// +/// # Template Arguments +/// +/// `St` is a type implementing [StorageValue]. You usually shouldn't implement this type manually. +/// Instead use the implementations generated by the [srml_support::decl_storage] macro. +/// +/// `T` is the type pointed to by SV. +/// +/// `Hash` is the block hash type for your runtime, used for specifying a particular block. +/// `Hash = ::Hash` +/// +/// ```no_run +/// # use jsonrpc_client_transports::RpcError; +/// # +/// # fn main() -> Result<(), RpcError> { +/// use substrate_rpc_custom::storage_value; +/// use substrate_test_runtime::Runtime; +/// use substrate_test_runtime::system::Authorities; +/// use substrate_rpc_api::state::StateClient; +/// use jsonrpc_core::futures::Future; +/// use jsonrpc_client_transports::transports::http; +/// +/// type Hash = ::Hash; +/// +/// let fut = http::connect("http://[::1]:9933") +/// .map(|conn| StateClient::::new(conn)) +/// .and_then(|cl| storage_value::(&cl, None)); +/// +/// tokio::runtime::Runtime::new().unwrap().block_on(fut)?; +/// # Ok(()) +/// # } +/// ``` +/// +/// # Note +/// +/// Storage items declared using the [srml_support::decl_storage] won't be accessable unless they +/// are declared public. +/// +/// ``` +/// # use srml_support::decl_storage; +/// # use srml_support::decl_module; +/// # use parity_scale_codec::Encode; +/// # use srml_system::Trait; +/// # +/// # struct TestRuntime; +/// # +/// # decl_module! { +/// # pub struct Module for enum Call where origin: T::Origin {} +/// # } +/// # +/// // Answers is private. +/// decl_storage! { +/// trait Store for Module as TestRuntime { +/// Answers: Vec; +/// } +/// } +/// # +/// # fn main() {} +/// ``` +/// +/// ``` +/// # use srml_support::decl_storage; +/// # use srml_support::decl_module; +/// # use parity_scale_codec::Encode; +/// # use srml_system::Trait; +/// # +/// # struct TestRuntime; +/// # +/// # decl_module! { +/// # pub struct Module for enum Call where origin: T::Origin {} +/// # } +/// # +/// // Use this instead. +/// decl_storage! { +/// trait Store for Module as TestRuntime { +/// pub Answers: Vec; +/// } +/// } +/// # +/// # fn main() {} +/// ``` +pub fn storage_value< + St: StorageValue, + T: FullCodec, + Hash: Send + Sync + 'static + DeserializeOwned + Serialize, +>( + state_client: &StateClient, + block_index: Option, +) -> impl Future, Error = RpcError> { + storage_get( + state_client, + StorageKey(St::storage_value_final_key().to_vec()), + block_index, + ) +} + +/// Uses a typed key to query a typed value from chain state. +/// +/// ```no_run +/// # use srml_support::decl_storage; +/// # use srml_support::decl_module; +/// # use parity_scale_codec::Encode; +/// # use srml_system::Trait; +/// # +/// # struct TestRuntime; +/// # +/// # decl_module! { +/// # pub struct Module for enum Call where origin: T::Origin {} +/// # } +/// # +/// decl_storage! { +/// trait Store for Module as TestRuntime { +/// pub Voxels: map (i64, i64, i64) => u8; +/// } +/// } +/// # +/// # use jsonrpc_client_transports::RpcError; +/// # +/// # fn main() -> Result<(), RpcError> { +/// # use substrate_rpc_custom::storage_map_value; +/// # use substrate_rpc_api::state::StateClient; +/// # use jsonrpc_core::futures::Future; +/// # use jsonrpc_client_transports::transports::http; +/// # +/// # // Hash would normally be ::Hash, but we don't have +/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. +/// # type Hash = (); +/// +/// let fut = http::connect("http://[::1]:9933") +/// .map(|conn| StateClient::::new(conn)) +/// .and_then(|cl| { +/// let loc = (0, 0, 0); +/// storage_map_value::(&cl, loc, None) +/// }); +/// +/// let ret: Option = tokio::runtime::Runtime::new().unwrap().block_on(fut)?; +/// # Ok(()) +/// # } +/// ``` +pub fn storage_map_value< + St: StorageMap, + K: FullCodec, + V: FullCodec, + Hash: Send + Sync + 'static + DeserializeOwned + Serialize, +>( + state_client: &StateClient, + key: K, + block_index: Option, +) -> impl Future, Error = RpcError> { + storage_get( + state_client, + StorageKey(St::storage_map_final_key(key).as_ref().to_vec()), + block_index, + ) +} + +/// Uses a typed key to query a typed value from chain state. +/// +/// ```no_run +/// # use srml_support::decl_storage; +/// # use srml_support::decl_module; +/// # use parity_scale_codec::Encode; +/// # use srml_system::Trait; +/// # +/// # struct TestRuntime; +/// # +/// # decl_module! { +/// # pub struct Module for enum Call where origin: T::Origin {} +/// # } +/// # +/// decl_storage! { +/// trait Store for Module as TestRuntime { +/// pub Voxels: linked_map (i64, i64, i64) => u8; +/// } +/// } +/// # +/// # use jsonrpc_client_transports::RpcError; +/// # +/// # fn main() -> Result<(), RpcError> { +/// # use substrate_rpc_custom::storage_linked_map_value; +/// # use substrate_rpc_api::state::StateClient; +/// # use jsonrpc_core::futures::Future; +/// # use jsonrpc_client_transports::transports::http; +/// # +/// # // Hash would normally be ::Hash, but we don't have +/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. +/// # type Hash = (); +/// +/// let fut = http::connect("http://[::1]:9933") +/// .map(|conn| StateClient::::new(conn)) +/// .and_then(|cl| { +/// let loc = (0, 0, 0); +/// storage_linked_map_value::(&cl, loc, None) +/// }); +/// +/// let ret: Option = tokio::runtime::Runtime::new().unwrap().block_on(fut)?; +/// # Ok(()) +/// # } +/// ``` +pub fn storage_linked_map_value< + St: StorageLinkedMap, + K: FullCodec, + V: FullCodec, + Hash: Send + Sync + 'static + DeserializeOwned + Serialize, +>( + state_client: &StateClient, + key: K, + block_index: Option, +) -> impl Future, Error = RpcError> { + storage_get( + state_client, + StorageKey(St::storage_linked_map_final_key(key).as_ref().to_vec()), + block_index, + ) +} + +/// Uses two typed keys to query a typed value from chain state. +/// +/// ```no_run +/// # use srml_support::decl_storage; +/// # use srml_support::decl_module; +/// # use parity_scale_codec::Encode; +/// # use srml_system::Trait; +/// # +/// # struct TestRuntime; +/// # +/// # decl_module! { +/// # pub struct Module for enum Call where origin: T::Origin {} +/// # } +/// # +/// decl_storage! { +/// trait Store for Module as TestRuntime { +/// pub ChunkedVoxels: double_map (u64, u64, u64), blake2_256(u16) => u8; +/// } +/// } +/// # +/// # use jsonrpc_client_transports::RpcError; +/// # +/// # fn main() -> Result<(), RpcError> { +/// # use substrate_rpc_custom::storage_double_map_value; +/// # use substrate_rpc_api::state::StateClient; +/// # use jsonrpc_core::futures::Future; +/// # use jsonrpc_client_transports::transports::http; +/// # +/// # // Hash would normally be ::Hash, but we don't have +/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. +/// # type Hash = (); +/// +/// let fut = http::connect("http://[::1]:9933") +/// .map(|conn| StateClient::::new(conn)) +/// .and_then(|cl| { +/// let chunk = (0, 0, 0); +/// let block_index = 63999; +/// storage_double_map_value::(&cl, chunk, block_index, None) +/// }); +/// +/// let ret: Option = tokio::runtime::Runtime::new().unwrap().block_on(fut)?; +/// # Ok(()) +/// # } +/// ``` +pub fn storage_double_map_value< + St: StorageDoubleMap, + K1: FullCodec, + K2: FullCodec, + V: FullCodec, + Hash: Send + Sync + 'static + DeserializeOwned + Serialize, +>( + state_client: &StateClient, + key1: K1, + key2: K2, + block_index: Option, +) -> impl Future, Error = RpcError> { + storage_get( + state_client, + StorageKey(St::storage_double_map_final_key(key1, key2)), + block_index, + ) +} + +/// Lookup a typed value from storage using an encoded key. +fn storage_get( + state_client: &StateClient, + key: StorageKey, + block_index: Option, +) -> impl Future, Error = RpcError> { + state_client.storage(key, block_index).and_then( + |opt_data: Option| -> Result, RpcError> { + opt_data + .map(|storage_data| V::decode_all(&storage_data.0)) + .transpose() + .map_err(|decode_err| RpcError::Other(decode_err.into())) + }, + ) +} diff --git a/core/test-runtime/src/.#system.rs b/core/test-runtime/src/.#system.rs new file mode 120000 index 0000000000000..b6167e5d6b6be --- /dev/null +++ b/core/test-runtime/src/.#system.rs @@ -0,0 +1 @@ +a@aslap.hsd1.or.comcast.net.29152 \ No newline at end of file diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index ba0f25590d793..849990064a183 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -47,7 +47,7 @@ decl_storage! { ParentHash get(fn parent_hash): Hash; NewAuthorities get(fn new_authorities): Option>; StorageDigest get(fn storage_digest): Option; - Authorities get(fn authorities) config(): Vec; + pub Authorities get(fn authorities) config(): Vec; } } From 79dba7fd65340c6a6e290d971f2e8b7173df1884 Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Thu, 7 Nov 2019 14:40:56 -0800 Subject: [PATCH 2/6] Update substrate-rpc-custom functions to use async await syntax. --- core/rpc/custom/src/lib.rs | 181 ++++++++++++++++-------------- core/test-runtime/src/.#system.rs | 1 - 2 files changed, 94 insertions(+), 88 deletions(-) delete mode 120000 core/test-runtime/src/.#system.rs diff --git a/core/rpc/custom/src/lib.rs b/core/rpc/custom/src/lib.rs index b51f09a69862d..a03c107c51f38 100644 --- a/core/rpc/custom/src/lib.rs +++ b/core/rpc/custom/src/lib.rs @@ -1,8 +1,8 @@ //! Combines [substrate_rpc_api::state::StateClient] with [srml_support::storage::generator] traits //! to provide strongly typed chain state queries over rpc. +use futures::compat::Future01CompatExt; use jsonrpc_client_transports::RpcError; -use jsonrpc_core::futures::Future; use parity_scale_codec::{DecodeAll, FullCodec}; use serde::{de::DeserializeOwned, Serialize}; use srml_support::storage::generator::{ @@ -33,22 +33,24 @@ use substrate_rpc_api::state::StateClient; /// /// ```no_run /// # use jsonrpc_client_transports::RpcError; +/// # use futures::compat::Compat; +/// # use futures::future::FutureExt; /// # /// # fn main() -> Result<(), RpcError> { +/// # tokio::runtime::Runtime::new().unwrap().block_on(Compat::new(test().boxed())) +/// # } +/// # async fn test() -> Result<(), RpcError> { /// use substrate_rpc_custom::storage_value; /// use substrate_test_runtime::Runtime; /// use substrate_test_runtime::system::Authorities; /// use substrate_rpc_api::state::StateClient; -/// use jsonrpc_core::futures::Future; /// use jsonrpc_client_transports::transports::http; -/// +/// use futures::compat::Future01CompatExt; /// type Hash = ::Hash; /// -/// let fut = http::connect("http://[::1]:9933") -/// .map(|conn| StateClient::::new(conn)) -/// .and_then(|cl| storage_value::(&cl, None)); -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(fut)?; +/// let conn = http::connect("http://[::1]:9933").compat().await?; +/// let cl = StateClient::::new(conn); +/// let ret: Option> = storage_value::(&cl, None).await?; /// # Ok(()) /// # } /// ``` @@ -101,19 +103,20 @@ use substrate_rpc_api::state::StateClient; /// # /// # fn main() {} /// ``` -pub fn storage_value< +pub async fn storage_value< St: StorageValue, T: FullCodec, Hash: Send + Sync + 'static + DeserializeOwned + Serialize, >( state_client: &StateClient, block_index: Option, -) -> impl Future, Error = RpcError> { +) -> Result, RpcError> { storage_get( state_client, StorageKey(St::storage_value_final_key().to_vec()), block_index, ) + .await } /// Uses a typed key to query a typed value from chain state. @@ -123,6 +126,22 @@ pub fn storage_value< /// # use srml_support::decl_module; /// # use parity_scale_codec::Encode; /// # use srml_system::Trait; +/// # use substrate_rpc_custom::storage_map_value; +/// # use substrate_rpc_api::state::StateClient; +/// # use jsonrpc_core::futures::Future; +/// # use jsonrpc_client_transports::transports::http; +/// # use jsonrpc_client_transports::RpcError; +/// # use futures::compat::Compat; +/// # use futures::future::FutureExt; +/// # use futures::compat::Future01CompatExt; +/// # +/// # // Hash would normally be ::Hash, but we don't have +/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. +/// # type Hash = (); +/// # +/// # fn main() -> Result<(), RpcError> { +/// # tokio::runtime::Runtime::new().unwrap().block_on(Compat::new(test().boxed())) +/// # } /// # /// # struct TestRuntime; /// # @@ -135,31 +154,16 @@ pub fn storage_value< /// pub Voxels: map (i64, i64, i64) => u8; /// } /// } -/// # -/// # use jsonrpc_client_transports::RpcError; -/// # -/// # fn main() -> Result<(), RpcError> { -/// # use substrate_rpc_custom::storage_map_value; -/// # use substrate_rpc_api::state::StateClient; -/// # use jsonrpc_core::futures::Future; -/// # use jsonrpc_client_transports::transports::http; -/// # -/// # // Hash would normally be ::Hash, but we don't have -/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. -/// # type Hash = (); -/// -/// let fut = http::connect("http://[::1]:9933") -/// .map(|conn| StateClient::::new(conn)) -/// .and_then(|cl| { -/// let loc = (0, 0, 0); -/// storage_map_value::(&cl, loc, None) -/// }); /// -/// let ret: Option = tokio::runtime::Runtime::new().unwrap().block_on(fut)?; +/// # async fn test() -> Result<(), RpcError> { +/// let conn = http::connect("http://[::1]:9933").compat().await?; +/// let cl = StateClient::::new(conn); +/// let loc = (0, 0, 0); +/// let ret: Option = storage_map_value::(&cl, loc, None).await?; /// # Ok(()) /// # } /// ``` -pub fn storage_map_value< +pub async fn storage_map_value< St: StorageMap, K: FullCodec, V: FullCodec, @@ -168,12 +172,13 @@ pub fn storage_map_value< state_client: &StateClient, key: K, block_index: Option, -) -> impl Future, Error = RpcError> { +) -> Result, RpcError> { storage_get( state_client, StorageKey(St::storage_map_final_key(key).as_ref().to_vec()), block_index, ) + .await } /// Uses a typed key to query a typed value from chain state. @@ -183,6 +188,22 @@ pub fn storage_map_value< /// # use srml_support::decl_module; /// # use parity_scale_codec::Encode; /// # use srml_system::Trait; +/// # use substrate_rpc_custom::storage_linked_map_value; +/// # use substrate_rpc_api::state::StateClient; +/// # use jsonrpc_core::futures::Future; +/// # use jsonrpc_client_transports::transports::http; +/// # use jsonrpc_client_transports::RpcError; +/// # use futures::compat::Compat; +/// # use futures::future::FutureExt; +/// # use futures::compat::Future01CompatExt; +/// # +/// # // Hash would normally be ::Hash, but we don't have +/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. +/// # type Hash = (); +/// # +/// # fn main() -> Result<(), RpcError> { +/// # tokio::runtime::Runtime::new().unwrap().block_on(Compat::new(test().boxed())) +/// # } /// # /// # struct TestRuntime; /// # @@ -195,31 +216,16 @@ pub fn storage_map_value< /// pub Voxels: linked_map (i64, i64, i64) => u8; /// } /// } -/// # -/// # use jsonrpc_client_transports::RpcError; -/// # -/// # fn main() -> Result<(), RpcError> { -/// # use substrate_rpc_custom::storage_linked_map_value; -/// # use substrate_rpc_api::state::StateClient; -/// # use jsonrpc_core::futures::Future; -/// # use jsonrpc_client_transports::transports::http; -/// # -/// # // Hash would normally be ::Hash, but we don't have -/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. -/// # type Hash = (); -/// -/// let fut = http::connect("http://[::1]:9933") -/// .map(|conn| StateClient::::new(conn)) -/// .and_then(|cl| { -/// let loc = (0, 0, 0); -/// storage_linked_map_value::(&cl, loc, None) -/// }); /// -/// let ret: Option = tokio::runtime::Runtime::new().unwrap().block_on(fut)?; +/// # async fn test() -> Result<(), RpcError> { +/// let conn = http::connect("http://[::1]:9933").compat().await?; +/// let cl = StateClient::::new(conn); +/// let loc = (0, 0, 0); +/// let ret: Option = storage_linked_map_value::(&cl, loc, None).await?; /// # Ok(()) /// # } /// ``` -pub fn storage_linked_map_value< +pub async fn storage_linked_map_value< St: StorageLinkedMap, K: FullCodec, V: FullCodec, @@ -228,12 +234,13 @@ pub fn storage_linked_map_value< state_client: &StateClient, key: K, block_index: Option, -) -> impl Future, Error = RpcError> { +) -> Result, RpcError> { storage_get( state_client, StorageKey(St::storage_linked_map_final_key(key).as_ref().to_vec()), block_index, ) + .await } /// Uses two typed keys to query a typed value from chain state. @@ -243,6 +250,22 @@ pub fn storage_linked_map_value< /// # use srml_support::decl_module; /// # use parity_scale_codec::Encode; /// # use srml_system::Trait; +/// # use substrate_rpc_custom::storage_double_map_value; +/// # use substrate_rpc_api::state::StateClient; +/// # use jsonrpc_core::futures::Future; +/// # use jsonrpc_client_transports::transports::http; +/// # use jsonrpc_client_transports::RpcError; +/// # use futures::compat::Compat; +/// # use futures::future::FutureExt; +/// # use futures::compat::Future01CompatExt; +/// # +/// # // Hash would normally be ::Hash, but we don't have +/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. +/// # type Hash = (); +/// # +/// # fn main() -> Result<(), RpcError> { +/// # tokio::runtime::Runtime::new().unwrap().block_on(Compat::new(test().boxed())) +/// # } /// # /// # struct TestRuntime; /// # @@ -255,32 +278,19 @@ pub fn storage_linked_map_value< /// pub ChunkedVoxels: double_map (u64, u64, u64), blake2_256(u16) => u8; /// } /// } -/// # -/// # use jsonrpc_client_transports::RpcError; -/// # -/// # fn main() -> Result<(), RpcError> { -/// # use substrate_rpc_custom::storage_double_map_value; -/// # use substrate_rpc_api::state::StateClient; -/// # use jsonrpc_core::futures::Future; -/// # use jsonrpc_client_transports::transports::http; -/// # -/// # // Hash would normally be ::Hash, but we don't have -/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. -/// # type Hash = (); /// -/// let fut = http::connect("http://[::1]:9933") -/// .map(|conn| StateClient::::new(conn)) -/// .and_then(|cl| { -/// let chunk = (0, 0, 0); -/// let block_index = 63999; -/// storage_double_map_value::(&cl, chunk, block_index, None) -/// }); -/// -/// let ret: Option = tokio::runtime::Runtime::new().unwrap().block_on(fut)?; +/// # async fn test() -> Result<(), RpcError> { +/// let conn = http::connect("http://[::1]:9933").compat().await?; +/// let cl = StateClient::::new(conn); +/// let chunk = (0, 0, 0); +/// let block_index = 63999; +/// let ret: Option = +/// storage_double_map_value::(&cl, chunk, block_index, None) +/// .await?; /// # Ok(()) /// # } /// ``` -pub fn storage_double_map_value< +pub async fn storage_double_map_value< St: StorageDoubleMap, K1: FullCodec, K2: FullCodec, @@ -291,26 +301,23 @@ pub fn storage_double_map_value< key1: K1, key2: K2, block_index: Option, -) -> impl Future, Error = RpcError> { +) -> Result, RpcError> { storage_get( state_client, StorageKey(St::storage_double_map_final_key(key1, key2)), block_index, ) + .await } /// Lookup a typed value from storage using an encoded key. -fn storage_get( +async fn storage_get( state_client: &StateClient, key: StorageKey, block_index: Option, -) -> impl Future, Error = RpcError> { - state_client.storage(key, block_index).and_then( - |opt_data: Option| -> Result, RpcError> { - opt_data - .map(|storage_data| V::decode_all(&storage_data.0)) - .transpose() - .map_err(|decode_err| RpcError::Other(decode_err.into())) - }, - ) +) -> Result, RpcError> { + let opt: Option = state_client.storage(key, block_index).compat().await?; + opt.map(|storage_data| V::decode_all(&storage_data.0)) + .transpose() + .map_err(|decode_err| RpcError::Other(decode_err.into())) } diff --git a/core/test-runtime/src/.#system.rs b/core/test-runtime/src/.#system.rs deleted file mode 120000 index b6167e5d6b6be..0000000000000 --- a/core/test-runtime/src/.#system.rs +++ /dev/null @@ -1 +0,0 @@ -a@aslap.hsd1.or.comcast.net.29152 \ No newline at end of file From d97988da35460797b2da20881977677808518d62 Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Fri, 8 Nov 2019 13:05:06 -0800 Subject: [PATCH 3/6] The implementation of substrate-rpc-custom was a bit verbose and repetitive. This commit makes the implementation simpler by intruducing a struct which represents query for a typed value in storage. The new struct is called StorageQuery. A StorageQuery wraps a raw StorageKey but is not directy constructable. To construct a StorageQuery, the user must supply an implementation of a srml_support::storage::generator trait such as StorageValue or StorageMap. A type implementing one of the generator traits can be aquired by: A) marking a storage item as pub within a call to decl_storage (recommended) or B) implementing one of the generator types manually. While option B may sometimes me necessary, it's not recommended because separate manual implementaions may lose sync with the original definition. --- core/rpc/custom/src/lib.rs | 350 +++++++++---------------------------- 1 file changed, 82 insertions(+), 268 deletions(-) diff --git a/core/rpc/custom/src/lib.rs b/core/rpc/custom/src/lib.rs index a03c107c51f38..6838a038d5baa 100644 --- a/core/rpc/custom/src/lib.rs +++ b/core/rpc/custom/src/lib.rs @@ -1,9 +1,10 @@ //! Combines [substrate_rpc_api::state::StateClient] with [srml_support::storage::generator] traits //! to provide strongly typed chain state queries over rpc. +use core::marker::PhantomData; use futures::compat::Future01CompatExt; use jsonrpc_client_transports::RpcError; -use parity_scale_codec::{DecodeAll, FullCodec}; +use parity_scale_codec::{DecodeAll, FullCodec, FullEncode}; use serde::{de::DeserializeOwned, Serialize}; use srml_support::storage::generator::{ StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue, @@ -11,124 +12,14 @@ use srml_support::storage::generator::{ use substrate_primitives_storage::{StorageData, StorageKey}; use substrate_rpc_api::state::StateClient; -/// Queries a typed global from chain state. -/// -/// # Arguments -/// -/// `state_client` is a json rpc client generated by in the [substrate_rpc_api::state] module -/// using [jsonrpc_derive::rpc]. -/// -/// `block_index` specifies the block for which storage is to be queried. If None, the latest block -/// is queried. -/// -/// # Template Arguments -/// -/// `St` is a type implementing [StorageValue]. You usually shouldn't implement this type manually. -/// Instead use the implementations generated by the [srml_support::decl_storage] macro. -/// -/// `T` is the type pointed to by SV. -/// -/// `Hash` is the block hash type for your runtime, used for specifying a particular block. -/// `Hash = ::Hash` +/// A typed query on chain state usable from an RPC client. /// /// ```no_run -/// # use jsonrpc_client_transports::RpcError; -/// # use futures::compat::Compat; -/// # use futures::future::FutureExt; -/// # -/// # fn main() -> Result<(), RpcError> { -/// # tokio::runtime::Runtime::new().unwrap().block_on(Compat::new(test().boxed())) -/// # } -/// # async fn test() -> Result<(), RpcError> { -/// use substrate_rpc_custom::storage_value; -/// use substrate_test_runtime::Runtime; -/// use substrate_test_runtime::system::Authorities; -/// use substrate_rpc_api::state::StateClient; -/// use jsonrpc_client_transports::transports::http; -/// use futures::compat::Future01CompatExt; -/// type Hash = ::Hash; -/// -/// let conn = http::connect("http://[::1]:9933").compat().await?; -/// let cl = StateClient::::new(conn); -/// let ret: Option> = storage_value::(&cl, None).await?; -/// # Ok(()) -/// # } -/// ``` -/// -/// # Note -/// -/// Storage items declared using the [srml_support::decl_storage] won't be accessable unless they -/// are declared public. -/// -/// ``` -/// # use srml_support::decl_storage; -/// # use srml_support::decl_module; +/// # use srml_support::{decl_storage, decl_module}; /// # use parity_scale_codec::Encode; /// # use srml_system::Trait; -/// # -/// # struct TestRuntime; -/// # -/// # decl_module! { -/// # pub struct Module for enum Call where origin: T::Origin {} -/// # } -/// # -/// // Answers is private. -/// decl_storage! { -/// trait Store for Module as TestRuntime { -/// Answers: Vec; -/// } -/// } -/// # -/// # fn main() {} -/// ``` -/// -/// ``` -/// # use srml_support::decl_storage; -/// # use srml_support::decl_module; -/// # use parity_scale_codec::Encode; -/// # use srml_system::Trait; -/// # -/// # struct TestRuntime; -/// # -/// # decl_module! { -/// # pub struct Module for enum Call where origin: T::Origin {} -/// # } -/// # -/// // Use this instead. -/// decl_storage! { -/// trait Store for Module as TestRuntime { -/// pub Answers: Vec; -/// } -/// } -/// # -/// # fn main() {} -/// ``` -pub async fn storage_value< - St: StorageValue, - T: FullCodec, - Hash: Send + Sync + 'static + DeserializeOwned + Serialize, ->( - state_client: &StateClient, - block_index: Option, -) -> Result, RpcError> { - storage_get( - state_client, - StorageKey(St::storage_value_final_key().to_vec()), - block_index, - ) - .await -} - -/// Uses a typed key to query a typed value from chain state. -/// -/// ```no_run -/// # use srml_support::decl_storage; -/// # use srml_support::decl_module; -/// # use parity_scale_codec::Encode; -/// # use srml_system::Trait; -/// # use substrate_rpc_custom::storage_map_value; +/// # use substrate_rpc_custom::StorageQuery; /// # use substrate_rpc_api::state::StateClient; -/// # use jsonrpc_core::futures::Future; /// # use jsonrpc_client_transports::transports::http; /// # use jsonrpc_client_transports::RpcError; /// # use futures::compat::Compat; @@ -149,175 +40,98 @@ pub async fn storage_value< /// # pub struct Module for enum Call where origin: T::Origin {} /// # } /// # +/// pub type Loc = (i64, i64, i64); +/// pub type Block = u8; +/// +/// // Note that all fields are marked pub. /// decl_storage! { /// trait Store for Module as TestRuntime { -/// pub Voxels: map (i64, i64, i64) => u8; +/// pub LastActionId: u64; +/// pub Voxels: map Loc => Block; +/// pub Actions: linked_map u64 => Loc; +/// pub Prefab: double_map u128, blake2_256((i8, i8, i8)) => Block; /// } /// } /// /// # async fn test() -> Result<(), RpcError> { /// let conn = http::connect("http://[::1]:9933").compat().await?; /// let cl = StateClient::::new(conn); -/// let loc = (0, 0, 0); -/// let ret: Option = storage_map_value::(&cl, loc, None).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn storage_map_value< - St: StorageMap, - K: FullCodec, - V: FullCodec, - Hash: Send + Sync + 'static + DeserializeOwned + Serialize, ->( - state_client: &StateClient, - key: K, - block_index: Option, -) -> Result, RpcError> { - storage_get( - state_client, - StorageKey(St::storage_map_final_key(key).as_ref().to_vec()), - block_index, - ) - .await -} - -/// Uses a typed key to query a typed value from chain state. /// -/// ```no_run -/// # use srml_support::decl_storage; -/// # use srml_support::decl_module; -/// # use parity_scale_codec::Encode; -/// # use srml_system::Trait; -/// # use substrate_rpc_custom::storage_linked_map_value; -/// # use substrate_rpc_api::state::StateClient; -/// # use jsonrpc_core::futures::Future; -/// # use jsonrpc_client_transports::transports::http; -/// # use jsonrpc_client_transports::RpcError; -/// # use futures::compat::Compat; -/// # use futures::future::FutureExt; -/// # use futures::compat::Future01CompatExt; -/// # -/// # // Hash would normally be ::Hash, but we don't have -/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. -/// # type Hash = (); -/// # -/// # fn main() -> Result<(), RpcError> { -/// # tokio::runtime::Runtime::new().unwrap().block_on(Compat::new(test().boxed())) -/// # } -/// # -/// # struct TestRuntime; -/// # -/// # decl_module! { -/// # pub struct Module for enum Call where origin: T::Origin {} -/// # } -/// # -/// decl_storage! { -/// trait Store for Module as TestRuntime { -/// pub Voxels: linked_map (i64, i64, i64) => u8; -/// } -/// } +/// let q = StorageQuery::value::(); +/// let _: Option = q.get(&cl, None).await?; /// -/// # async fn test() -> Result<(), RpcError> { -/// let conn = http::connect("http://[::1]:9933").compat().await?; -/// let cl = StateClient::::new(conn); -/// let loc = (0, 0, 0); -/// let ret: Option = storage_linked_map_value::(&cl, loc, None).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn storage_linked_map_value< - St: StorageLinkedMap, - K: FullCodec, - V: FullCodec, - Hash: Send + Sync + 'static + DeserializeOwned + Serialize, ->( - state_client: &StateClient, - key: K, - block_index: Option, -) -> Result, RpcError> { - storage_get( - state_client, - StorageKey(St::storage_linked_map_final_key(key).as_ref().to_vec()), - block_index, - ) - .await -} - -/// Uses two typed keys to query a typed value from chain state. +/// let q = StorageQuery::map::((0, 0, 0)); +/// let _: Option = q.get(&cl, None).await?; /// -/// ```no_run -/// # use srml_support::decl_storage; -/// # use srml_support::decl_module; -/// # use parity_scale_codec::Encode; -/// # use srml_system::Trait; -/// # use substrate_rpc_custom::storage_double_map_value; -/// # use substrate_rpc_api::state::StateClient; -/// # use jsonrpc_core::futures::Future; -/// # use jsonrpc_client_transports::transports::http; -/// # use jsonrpc_client_transports::RpcError; -/// # use futures::compat::Compat; -/// # use futures::future::FutureExt; -/// # use futures::compat::Future01CompatExt; -/// # -/// # // Hash would normally be ::Hash, but we don't have -/// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. -/// # type Hash = (); -/// # -/// # fn main() -> Result<(), RpcError> { -/// # tokio::runtime::Runtime::new().unwrap().block_on(Compat::new(test().boxed())) -/// # } -/// # -/// # struct TestRuntime; -/// # -/// # decl_module! { -/// # pub struct Module for enum Call where origin: T::Origin {} -/// # } -/// # -/// decl_storage! { -/// trait Store for Module as TestRuntime { -/// pub ChunkedVoxels: double_map (u64, u64, u64), blake2_256(u16) => u8; -/// } -/// } +/// let q = StorageQuery::linked_map::(12); +/// let _: Option = q.get(&cl, None).await?; /// -/// # async fn test() -> Result<(), RpcError> { -/// let conn = http::connect("http://[::1]:9933").compat().await?; -/// let cl = StateClient::::new(conn); -/// let chunk = (0, 0, 0); -/// let block_index = 63999; -/// let ret: Option = -/// storage_double_map_value::(&cl, chunk, block_index, None) -/// .await?; +/// let q = StorageQuery::double_map::(3, (0, 0, 0)); +/// let _: Option = q.get(&cl, None).await?; +/// # /// # Ok(()) /// # } /// ``` -pub async fn storage_double_map_value< - St: StorageDoubleMap, - K1: FullCodec, - K2: FullCodec, - V: FullCodec, - Hash: Send + Sync + 'static + DeserializeOwned + Serialize, ->( - state_client: &StateClient, - key1: K1, - key2: K2, - block_index: Option, -) -> Result, RpcError> { - storage_get( - state_client, - StorageKey(St::storage_double_map_final_key(key1, key2)), - block_index, - ) - .await +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct StorageQuery { + key: StorageKey, + _spook: PhantomData, } -/// Lookup a typed value from storage using an encoded key. -async fn storage_get( - state_client: &StateClient, - key: StorageKey, - block_index: Option, -) -> Result, RpcError> { - let opt: Option = state_client.storage(key, block_index).compat().await?; - opt.map(|storage_data| V::decode_all(&storage_data.0)) - .transpose() - .map_err(|decode_err| RpcError::Other(decode_err.into())) +impl StorageQuery { + /// Create a storage query for a StorageValue. + pub fn value>() -> Self { + Self { + key: StorageKey(St::storage_value_final_key().to_vec()), + _spook: PhantomData, + } + } + + /// Create a storage query for a value in a StorageMap. + pub fn map, K: FullEncode>(key: K) -> Self { + Self { + key: StorageKey(St::storage_map_final_key(key).as_ref().to_vec()), + _spook: PhantomData, + } + } + + /// Create a storage query for a value in a StorageLinkedMap. + pub fn linked_map, K: FullCodec>(key: K) -> Self { + Self { + key: StorageKey(St::storage_linked_map_final_key(key).as_ref().to_vec()), + _spook: PhantomData, + } + } + + /// Create a storage query for a value in a StorageDoubleMap. + pub fn double_map, K1: FullEncode, K2: FullEncode>( + key1: K1, + key2: K2, + ) -> Self { + Self { + key: StorageKey(St::storage_double_map_final_key(key1, key2)), + _spook: PhantomData, + } + } + + /// Send this query over RPC, await the typed result. + /// + /// Hash should be ::Hash. + /// + /// # Arguments + /// + /// state_client represents a connection to the RPC server. + /// + /// block_index indicates the block for which state will be queried. A value of None indicates the + /// latest block. + pub async fn get( + self, + state_client: &StateClient, + block_index: Option, + ) -> Result, RpcError> { + let opt: Option = state_client.storage(self.key, block_index).compat().await?; + opt.map(|encoded| V::decode_all(&encoded.0)) + .transpose() + .map_err(|decode_err| RpcError::Other(decode_err.into())) + } } From 250fff0075a1e945553bed2125e07e278db2a989 Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Fri, 8 Nov 2019 13:45:43 -0800 Subject: [PATCH 4/6] drop unused dependency --- Cargo.lock | 16 ++++++++++++++-- core/rpc/custom/Cargo.toml | 1 - 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 676ed2e9fafe2..9a0bcdfcddc5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1673,6 +1673,18 @@ dependencies = [ "webpki-roots 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hyper-tls" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.1.5" @@ -5922,12 +5934,11 @@ dependencies = [ "jsonrpc-client-transports 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", "srml-support 2.0.0", "srml-system 2.0.0", "substrate-primitives-storage 2.0.0", "substrate-rpc-api 2.0.0", - "substrate-test-runtime 2.0.0", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -7566,6 +7577,7 @@ dependencies = [ "checksum hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" "checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" "checksum hyper-rustls 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "719d85c7df4a7f309a77d145340a063ea929dcb2e025bae46a80345cffec2952" +"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3fa0086251524c50fd53b32e7b05eb6d79e2f97221eaf0c53c0ca9c3096f21d3" diff --git a/core/rpc/custom/Cargo.toml b/core/rpc/custom/Cargo.toml index 46bcf640be1a4..c7c6c2f11eca2 100644 --- a/core/rpc/custom/Cargo.toml +++ b/core/rpc/custom/Cargo.toml @@ -15,7 +15,6 @@ futures-preview = { version = "0.3.0-alpha.19", features = ["compat"] } serde = "1" [dev-dependencies] -substrate-test-runtime = { path = "../../../core/test-runtime" } srml-system = { path = "../../../srml/system" } srml-support = { path = "../../../srml/support" } jsonrpc-client-transports = { version = "14", features = ["http"] } From 47277787b1c476456b3e161a30ab076c103b88d0 Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Fri, 8 Nov 2019 14:21:11 -0800 Subject: [PATCH 5/6] fmt --- core/rpc/custom/src/lib.rs | 108 ++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/core/rpc/custom/src/lib.rs b/core/rpc/custom/src/lib.rs index 6838a038d5baa..57e0acbf3f3fc 100644 --- a/core/rpc/custom/src/lib.rs +++ b/core/rpc/custom/src/lib.rs @@ -7,7 +7,7 @@ use jsonrpc_client_transports::RpcError; use parity_scale_codec::{DecodeAll, FullCodec, FullEncode}; use serde::{de::DeserializeOwned, Serialize}; use srml_support::storage::generator::{ - StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue, + StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue, }; use substrate_primitives_storage::{StorageData, StorageKey}; use substrate_rpc_api::state::StateClient; @@ -74,64 +74,64 @@ use substrate_rpc_api::state::StateClient; /// ``` #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct StorageQuery { - key: StorageKey, - _spook: PhantomData, + key: StorageKey, + _spook: PhantomData, } impl StorageQuery { - /// Create a storage query for a StorageValue. - pub fn value>() -> Self { - Self { - key: StorageKey(St::storage_value_final_key().to_vec()), - _spook: PhantomData, - } - } + /// Create a storage query for a StorageValue. + pub fn value>() -> Self { + Self { + key: StorageKey(St::storage_value_final_key().to_vec()), + _spook: PhantomData, + } + } - /// Create a storage query for a value in a StorageMap. - pub fn map, K: FullEncode>(key: K) -> Self { - Self { - key: StorageKey(St::storage_map_final_key(key).as_ref().to_vec()), - _spook: PhantomData, - } - } + /// Create a storage query for a value in a StorageMap. + pub fn map, K: FullEncode>(key: K) -> Self { + Self { + key: StorageKey(St::storage_map_final_key(key).as_ref().to_vec()), + _spook: PhantomData, + } + } - /// Create a storage query for a value in a StorageLinkedMap. - pub fn linked_map, K: FullCodec>(key: K) -> Self { - Self { - key: StorageKey(St::storage_linked_map_final_key(key).as_ref().to_vec()), - _spook: PhantomData, - } - } + /// Create a storage query for a value in a StorageLinkedMap. + pub fn linked_map, K: FullCodec>(key: K) -> Self { + Self { + key: StorageKey(St::storage_linked_map_final_key(key).as_ref().to_vec()), + _spook: PhantomData, + } + } - /// Create a storage query for a value in a StorageDoubleMap. - pub fn double_map, K1: FullEncode, K2: FullEncode>( - key1: K1, - key2: K2, - ) -> Self { - Self { - key: StorageKey(St::storage_double_map_final_key(key1, key2)), - _spook: PhantomData, - } - } + /// Create a storage query for a value in a StorageDoubleMap. + pub fn double_map, K1: FullEncode, K2: FullEncode>( + key1: K1, + key2: K2, + ) -> Self { + Self { + key: StorageKey(St::storage_double_map_final_key(key1, key2)), + _spook: PhantomData, + } + } - /// Send this query over RPC, await the typed result. - /// - /// Hash should be ::Hash. - /// - /// # Arguments - /// - /// state_client represents a connection to the RPC server. - /// - /// block_index indicates the block for which state will be queried. A value of None indicates the - /// latest block. - pub async fn get( - self, - state_client: &StateClient, - block_index: Option, - ) -> Result, RpcError> { - let opt: Option = state_client.storage(self.key, block_index).compat().await?; - opt.map(|encoded| V::decode_all(&encoded.0)) - .transpose() - .map_err(|decode_err| RpcError::Other(decode_err.into())) - } + /// Send this query over RPC, await the typed result. + /// + /// Hash should be ::Hash. + /// + /// # Arguments + /// + /// state_client represents a connection to the RPC server. + /// + /// block_index indicates the block for which state will be queried. A value of None indicates the + /// latest block. + pub async fn get( + self, + state_client: &StateClient, + block_index: Option, + ) -> Result, RpcError> { + let opt: Option = state_client.storage(self.key, block_index).compat().await?; + opt.map(|encoded| V::decode_all(&encoded.0)) + .transpose() + .map_err(|decode_err| RpcError::Other(decode_err.into())) + } } From 5fc020113364b90cd18941acbcf18d2caf581fd3 Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Fri, 8 Nov 2019 14:32:50 -0800 Subject: [PATCH 6/6] Remove unnecessary pub from Authorities field in test-runtime storage declaration. This field was added to support a test in an earlier commit. The test no longer relies on test-runtime so the change can be reverted. --- core/test-runtime/src/system.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index 849990064a183..ba0f25590d793 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -47,7 +47,7 @@ decl_storage! { ParentHash get(fn parent_hash): Hash; NewAuthorities get(fn new_authorities): Option>; StorageDigest get(fn storage_digest): Option; - pub Authorities get(fn authorities) config(): Vec; + Authorities get(fn authorities) config(): Vec; } }