From 951d911a60c3c126b75da8fb5152c42abd677ad2 Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Wed, 6 Nov 2019 17:43:17 -0800 Subject: [PATCH 1/9] 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/9] 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/9] 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/9] 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/9] 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/9] 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; } } From 18070e2580c4b7d03e346c8dcc7631140302e7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sun, 10 Nov 2019 18:50:01 +0100 Subject: [PATCH 7/9] Move it to srml as support extension. --- Cargo.lock | 32 ++++++++--------- Cargo.toml | 2 +- core/rpc/custom/Cargo.toml | 21 ----------- srml/support/rpc/Cargo.toml | 18 ++++++++++ .../custom => srml/support/rpc}/src/lib.rs | 36 +++++++++++++------ 5 files changed, 61 insertions(+), 48 deletions(-) delete mode 100644 core/rpc/custom/Cargo.toml create mode 100644 srml/support/rpc/Cargo.toml rename {core/rpc/custom => srml/support/rpc}/src/lib.rs (84%) diff --git a/Cargo.lock b/Cargo.lock index 9a0bcdfcddc5a..30d530f79d154 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1345,6 +1345,7 @@ name = "futures-util" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures-io 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4901,6 +4902,21 @@ dependencies = [ "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "srml-support-rpc" +version = "0.1.0" +dependencies = [ + "futures 0.3.0 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", + "srml-support 2.0.0", + "substrate-primitives-storage 2.0.0", + "substrate-rpc-api 2.0.0", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "srml-support-test" version = "2.0.0" @@ -5926,22 +5942,6 @@ 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.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", - "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 4cb358ef55907..678ed290508a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ members = [ "core/panic-handler", "core/primitives", "core/rpc", - "core/rpc/custom", "core/rpc/primitives", "core/rpc-servers", "core/serializer", @@ -55,6 +54,7 @@ members = [ "core/utils/wasm-builder-runner", "core/wasm-interface", "srml/support", + "srml/support/rpc", "srml/support/procedural", "srml/support/procedural/tools", "srml/support/procedural/tools/derive", diff --git a/core/rpc/custom/Cargo.toml b/core/rpc/custom/Cargo.toml deleted file mode 100644 index c7c6c2f11eca2..0000000000000 --- a/core/rpc/custom/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[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] -srml-system = { path = "../../../srml/system" } -srml-support = { path = "../../../srml/support" } -jsonrpc-client-transports = { version = "14", features = ["http"] } -tokio = "0.1" diff --git a/srml/support/rpc/Cargo.toml b/srml/support/rpc/Cargo.toml new file mode 100644 index 0000000000000..196dfab45a406 --- /dev/null +++ b/srml/support/rpc/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "srml-support-rpc" +version = "0.1.0" +authors = ["Parity Technologies ", "Andrew Dirksen "] +edition = "2018" + +[dependencies] +futures = { version = "0.3.0", features = ["compat"] } +jsonrpc-client-transports = "14" +jsonrpc-core = "14" +parity-scale-codec = "1" +serde = "1" +srml-support = { path = "../" } +substrate-primitives-storage = { path = "../../../core/primitives/storage" } +substrate-rpc-api = { path = "../../../core/rpc/api" } + +[dev-dependencies] +tokio = "0.1" diff --git a/core/rpc/custom/src/lib.rs b/srml/support/rpc/src/lib.rs similarity index 84% rename from core/rpc/custom/src/lib.rs rename to srml/support/rpc/src/lib.rs index 57e0acbf3f3fc..b1211974371d0 100644 --- a/core/rpc/custom/src/lib.rs +++ b/srml/support/rpc/src/lib.rs @@ -1,30 +1,46 @@ +// Copyright 2019 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 . + //! Combines [substrate_rpc_api::state::StateClient] with [srml_support::storage::generator] traits //! to provide strongly typed chain state queries over rpc. +#![warn(missing_docs)] + use core::marker::PhantomData; use futures::compat::Future01CompatExt; 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, -}; +use srml_support::storage::generator::{StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue}; use substrate_primitives_storage::{StorageData, StorageKey}; use substrate_rpc_api::state::StateClient; /// A typed query on chain state usable from an RPC client. /// /// ```no_run -/// # use srml_support::{decl_storage, decl_module}; +/// # use futures::compat::Compat; +/// # use futures::compat::Future01CompatExt; +/// # use futures::future::FutureExt; +/// # use jsonrpc_client_transports::RpcError; +/// # use jsonrpc_client_transports::transports::http; /// # use parity_scale_codec::Encode; +/// # use srml_support::{decl_storage, decl_module}; /// # use srml_system::Trait; -/// # use substrate_rpc_custom::StorageQuery; /// # use substrate_rpc_api::state::StateClient; -/// # use jsonrpc_client_transports::transports::http; -/// # use jsonrpc_client_transports::RpcError; -/// # use futures::compat::Compat; -/// # use futures::future::FutureExt; -/// # use futures::compat::Future01CompatExt; +/// # use substrate_rpc_custom::StorageQuery; /// # /// # // Hash would normally be ::Hash, but we don't have /// # // srml_system::Trait implemented for TestRuntime. Here we just pretend. From e6a83ab61c07605242b740098cd7b8679d9b7a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sun, 10 Nov 2019 18:55:01 +0100 Subject: [PATCH 8/9] Fix long lines. --- srml/support/rpc/src/lib.rs | 8 +++++--- srml/support/src/lib.rs | 15 ++++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/srml/support/rpc/src/lib.rs b/srml/support/rpc/src/lib.rs index b1211974371d0..a0c60eae14164 100644 --- a/srml/support/rpc/src/lib.rs +++ b/srml/support/rpc/src/lib.rs @@ -24,7 +24,9 @@ use futures::compat::Future01CompatExt; 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}; +use srml_support::storage::generator::{ + StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue +}; use substrate_primitives_storage::{StorageData, StorageKey}; use substrate_rpc_api::state::StateClient; @@ -138,8 +140,8 @@ impl StorageQuery { /// /// 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. + /// 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, diff --git a/srml/support/src/lib.rs b/srml/support/src/lib.rs index cfe6487203ddd..3948794f07316 100644 --- a/srml/support/src/lib.rs +++ b/srml/support/src/lib.rs @@ -262,16 +262,21 @@ mod tests { decl_storage! { trait Store for Module as Example { - pub Data get(fn data) build(|_| vec![(15u32, 42u64)]): linked_map hasher(twox_64_concat) u32 => u64; + pub Data get(fn data) build(|_| vec![(15u32, 42u64)]): + linked_map hasher(twox_64_concat) u32 => u64; pub OptionLinkedMap: linked_map u32 => Option; - pub GenericData get(fn generic_data): linked_map hasher(twox_128) T::BlockNumber => T::BlockNumber; - pub GenericData2 get(fn generic_data2): linked_map T::BlockNumber => Option; + pub GenericData get(fn generic_data): + linked_map hasher(twox_128) T::BlockNumber => T::BlockNumber; + pub GenericData2 get(fn generic_data2): + linked_map T::BlockNumber => Option; pub GetterNoFnKeyword get(no_fn): Option; pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]): double_map hasher(twox_64_concat) u32, blake2_256(u32) => u64; - pub GenericDataDM: double_map T::BlockNumber, twox_128(T::BlockNumber) => T::BlockNumber; - pub GenericData2DM: double_map T::BlockNumber, twox_256(T::BlockNumber) => Option; + pub GenericDataDM: + double_map T::BlockNumber, twox_128(T::BlockNumber) => T::BlockNumber; + pub GenericData2DM: + double_map T::BlockNumber, twox_256(T::BlockNumber) => Option; pub AppendableDM: double_map u32, blake2_256(T::BlockNumber) => Vec; } } From 82f34588d8235611635630c4cfb56e7ebf6e4c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sun, 10 Nov 2019 21:17:54 +0100 Subject: [PATCH 9/9] Fix. --- Cargo.lock | 3 ++- srml/support/rpc/Cargo.toml | 3 ++- srml/support/rpc/src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30d530f79d154..b41f6eb4c5606 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4904,7 +4904,7 @@ dependencies = [ [[package]] name = "srml-support-rpc" -version = "0.1.0" +version = "2.0.0" dependencies = [ "futures 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-client-transports 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4912,6 +4912,7 @@ dependencies = [ "parity-scale-codec 1.0.6 (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", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/srml/support/rpc/Cargo.toml b/srml/support/rpc/Cargo.toml index 196dfab45a406..fca9634ef1fba 100644 --- a/srml/support/rpc/Cargo.toml +++ b/srml/support/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "srml-support-rpc" -version = "0.1.0" +version = "2.0.0" authors = ["Parity Technologies ", "Andrew Dirksen "] edition = "2018" @@ -15,4 +15,5 @@ substrate-primitives-storage = { path = "../../../core/primitives/storage" } substrate-rpc-api = { path = "../../../core/rpc/api" } [dev-dependencies] +srml-system = { path = "../../system" } tokio = "0.1" diff --git a/srml/support/rpc/src/lib.rs b/srml/support/rpc/src/lib.rs index a0c60eae14164..f2a6182cd239f 100644 --- a/srml/support/rpc/src/lib.rs +++ b/srml/support/rpc/src/lib.rs @@ -40,9 +40,9 @@ use substrate_rpc_api::state::StateClient; /// # use jsonrpc_client_transports::transports::http; /// # use parity_scale_codec::Encode; /// # use srml_support::{decl_storage, decl_module}; +/// # use srml_support_rpc::StorageQuery; /// # use srml_system::Trait; /// # use substrate_rpc_api::state::StateClient; -/// # use substrate_rpc_custom::StorageQuery; /// # /// # // Hash would normally be ::Hash, but we don't have /// # // srml_system::Trait implemented for TestRuntime. Here we just pretend.