diff --git a/Cargo.lock b/Cargo.lock index 34d2296e019ce..3ed09f3ff7911 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4830,6 +4830,7 @@ dependencies = [ "sc-finality-grandpa-rpc", "sc-rpc", "sc-rpc-api", + "sc-rpc-spec-v2", "sc-sync-state-rpc", "sc-transaction-pool-api", "sp-api", @@ -8756,6 +8757,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "sc-rpc-spec-v2" +version = "0.10.0-dev" +dependencies = [ + "hex", + "jsonrpsee", + "sc-chain-spec", + "serde_json", + "tokio", +] + [[package]] name = "sc-runtime-test" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index 4dbf65dc7e1fe..48a31940fd3bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ members = [ "client/rpc", "client/rpc-api", "client/rpc-servers", + "client/rpc-spec-v2", "client/service", "client/service/test", "client/state-db", diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index 851eb2cfc5104..0b69ae27010fa 100644 --- a/bin/node/rpc/Cargo.toml +++ b/bin/node/rpc/Cargo.toml @@ -26,6 +26,7 @@ sc-finality-grandpa = { version = "0.10.0-dev", path = "../../../client/finality sc-finality-grandpa-rpc = { version = "0.10.0-dev", path = "../../../client/finality-grandpa/rpc" } sc-rpc = { version = "4.0.0-dev", path = "../../../client/rpc" } sc-rpc-api = { version = "0.10.0-dev", path = "../../../client/rpc-api" } +sc-rpc-spec-v2 = { version = "0.10.0-dev", path = "../../../client/rpc-spec-v2" } sc-sync-state-rpc = { version = "0.10.0-dev", path = "../../../client/sync-state-rpc" } sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 1c8b9cce1a744..0e6b04087fa63 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -124,6 +124,7 @@ where use sc_consensus_babe_rpc::{Babe, BabeApiServer}; use sc_finality_grandpa_rpc::{Grandpa, GrandpaApiServer}; use sc_rpc::dev::{Dev, DevApiServer}; + use sc_rpc_spec_v2::chain_spec::{ChainSpec, ChainSpecApiServer}; use sc_sync_state_rpc::{SyncState, SyncStateApiServer}; use substrate_frame_rpc_system::{System, SystemApiServer}; use substrate_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer}; @@ -140,6 +141,11 @@ where finality_provider, } = grandpa; + let chain_name = chain_spec.name().to_string(); + let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); + let properties = chain_spec.properties(); + io.merge(ChainSpec::new(chain_name, genesis_hash, properties).into_rpc())?; + io.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; // Making synchronous calls in light client freezes the browser currently, // more context: https://github.com/paritytech/substrate/pull/3480 diff --git a/client/rpc-servers/src/lib.rs b/client/rpc-servers/src/lib.rs index 68b4aa6767348..0e2aca0dcc829 100644 --- a/client/rpc-servers/src/lib.rs +++ b/client/rpc-servers/src/lib.rs @@ -203,7 +203,6 @@ fn build_rpc_api(mut rpc_api: RpcModule) -> RpcModu rpc_api .register_method("rpc_methods", move |_, _| { Ok(serde_json::json!({ - "version": 1, "methods": available_methods, })) }) diff --git a/client/rpc-spec-v2/Cargo.toml b/client/rpc-spec-v2/Cargo.toml new file mode 100644 index 0000000000000..12dec7464e6d0 --- /dev/null +++ b/client/rpc-spec-v2/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sc-rpc-spec-v2" +version = "0.10.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Substrate RPC interface v2." +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +jsonrpsee = { version = "0.15.1", features = ["server", "macros"] } +# Internal chain structures for "chain_spec". +sc-chain-spec = { version = "4.0.0-dev", path = "../chain-spec" } +hex = "0.4" + +[dev-dependencies] +serde_json = "1.0" +tokio = { version = "1.17.0", features = ["macros"] } diff --git a/client/rpc-spec-v2/README.md b/client/rpc-spec-v2/README.md new file mode 100644 index 0000000000000..e860e0c2334da --- /dev/null +++ b/client/rpc-spec-v2/README.md @@ -0,0 +1,5 @@ +Substrate RPC interfaces. + +A collection of RPC methods and subscriptions supported by all substrate clients. + +License: GPL-3.0-or-later WITH Classpath-exception-2.0 \ No newline at end of file diff --git a/client/rpc-spec-v2/src/chain_spec/api.rs b/client/rpc-spec-v2/src/chain_spec/api.rs new file mode 100644 index 0000000000000..dfe2d76de6501 --- /dev/null +++ b/client/rpc-spec-v2/src/chain_spec/api.rs @@ -0,0 +1,53 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! API trait of the chain spec. + +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use sc_chain_spec::Properties; + +#[rpc(client, server)] +pub trait ChainSpecApi { + /// Get the chain name, as present in the chain specification. + /// + /// # Unstable + /// + /// This method is unstable and subject to change in the future. + #[method(name = "chainSpec_unstable_chainName")] + fn chain_spec_unstable_chain_name(&self) -> RpcResult; + + /// Get the chain's genesis hash. + /// + /// # Unstable + /// + /// This method is unstable and subject to change in the future. + #[method(name = "chainSpec_unstable_genesisHash")] + fn chain_spec_unstable_genesis_hash(&self) -> RpcResult; + + /// Get the properties of the chain, as present in the chain specification. + /// + /// # Note + /// + /// The json whitespaces are not guaranteed to persist. + /// + /// # Unstable + /// + /// This method is unstable and subject to change in the future. + #[method(name = "chainSpec_unstable_properties")] + fn chain_spec_unstable_properties(&self) -> RpcResult; +} diff --git a/client/rpc-spec-v2/src/chain_spec/chain_spec.rs b/client/rpc-spec-v2/src/chain_spec/chain_spec.rs new file mode 100644 index 0000000000000..90d05f1d9d41d --- /dev/null +++ b/client/rpc-spec-v2/src/chain_spec/chain_spec.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! API implementation for the specification of a chain. + +use crate::chain_spec::api::ChainSpecApiServer; +use jsonrpsee::core::RpcResult; +use sc_chain_spec::Properties; + +/// An API for chain spec RPC calls. +pub struct ChainSpec { + /// The name of the chain. + name: String, + /// The hexadecimal encoded hash of the genesis block. + genesis_hash: String, + /// Chain properties. + properties: Properties, +} + +impl ChainSpec { + /// Creates a new [`ChainSpec`]. + pub fn new>( + name: String, + genesis_hash: Hash, + properties: Properties, + ) -> Self { + let genesis_hash = format!("0x{}", hex::encode(genesis_hash)); + + Self { name, properties, genesis_hash } + } +} + +impl ChainSpecApiServer for ChainSpec { + fn chain_spec_unstable_chain_name(&self) -> RpcResult { + Ok(self.name.clone()) + } + + fn chain_spec_unstable_genesis_hash(&self) -> RpcResult { + Ok(self.genesis_hash.clone()) + } + + fn chain_spec_unstable_properties(&self) -> RpcResult { + Ok(self.properties.clone()) + } +} diff --git a/client/rpc-spec-v2/src/chain_spec/mod.rs b/client/rpc-spec-v2/src/chain_spec/mod.rs new file mode 100644 index 0000000000000..cd4fcf246f603 --- /dev/null +++ b/client/rpc-spec-v2/src/chain_spec/mod.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Substrate chain specification API. +//! +//! The *chain spec* (short for *chain specification*) allows inspecting the content of +//! the specification of the chain that a JSON-RPC server is targeting. +//! +//! The values returned by the API are guaranteed to never change during the lifetime of the +//! JSON-RPC server. +//! +//! # Note +//! +//! Methods are prefixed by `chainSpec`. + +#[cfg(test)] +mod tests; + +pub mod api; +pub mod chain_spec; + +pub use api::ChainSpecApiServer; +pub use chain_spec::ChainSpec; diff --git a/client/rpc-spec-v2/src/chain_spec/tests.rs b/client/rpc-spec-v2/src/chain_spec/tests.rs new file mode 100644 index 0000000000000..6c078b2974e98 --- /dev/null +++ b/client/rpc-spec-v2/src/chain_spec/tests.rs @@ -0,0 +1,61 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::*; +use jsonrpsee::{types::EmptyParams, RpcModule}; +use sc_chain_spec::Properties; + +const CHAIN_NAME: &'static str = "TEST_CHAIN_NAME"; +const CHAIN_GENESIS: [u8; 32] = [0; 32]; +const CHAIN_PROPERTIES: &'static str = r#"{"three": "123", "one": 1, "two": 12}"#; + +fn api() -> RpcModule { + ChainSpec::new( + CHAIN_NAME.to_string(), + CHAIN_GENESIS, + serde_json::from_str(CHAIN_PROPERTIES).unwrap(), + ) + .into_rpc() +} + +#[tokio::test] +async fn chain_spec_chain_name_works() { + let name = api() + .call::<_, String>("chainSpec_unstable_chainName", EmptyParams::new()) + .await + .unwrap(); + assert_eq!(name, CHAIN_NAME); +} + +#[tokio::test] +async fn chain_spec_genesis_hash_works() { + let genesis = api() + .call::<_, String>("chainSpec_unstable_genesisHash", EmptyParams::new()) + .await + .unwrap(); + assert_eq!(genesis, format!("0x{}", hex::encode(CHAIN_GENESIS))); +} + +#[tokio::test] +async fn chain_spec_properties_works() { + let properties = api() + .call::<_, Properties>("chainSpec_unstable_properties", EmptyParams::new()) + .await + .unwrap(); + assert_eq!(properties, serde_json::from_str(CHAIN_PROPERTIES).unwrap()); +} diff --git a/client/rpc-spec-v2/src/lib.rs b/client/rpc-spec-v2/src/lib.rs new file mode 100644 index 0000000000000..297fda13172d6 --- /dev/null +++ b/client/rpc-spec-v2/src/lib.rs @@ -0,0 +1,26 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Substrate JSON-RPC interface v2. +//! +//! Specification [document](https://paritytech.github.io/json-rpc-interface-spec/). + +#![warn(missing_docs)] +#![deny(unused_crate_dependencies)] + +pub mod chain_spec;