diff --git a/Cargo.lock b/Cargo.lock index b801da264363a..b10e2074167a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3826,6 +3826,7 @@ dependencies = [ "node-runtime", "pallet-contracts-rpc", "pallet-transaction-payment-rpc", + "sc-chain-spec", "sc-client-api", "sc-consensus-babe", "sc-consensus-babe-rpc", @@ -3835,6 +3836,7 @@ dependencies = [ "sc-keystore", "sc-rpc", "sc-rpc-api", + "sc-sync-state-rpc", "sp-api", "sp-block-builder", "sp-blockchain", @@ -6315,11 +6317,15 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "sc-chain-spec-derive", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-finality-grandpa", "sc-network", "sc-telemetry", "serde", "serde_json", "sp-chain-spec", + "sp-consensus-babe", "sp-core", "sp-runtime", ] @@ -6357,6 +6363,9 @@ dependencies = [ "regex", "rpassword", "sc-client-api", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-finality-grandpa", "sc-informant", "sc-keystore", "sc-network", @@ -7284,6 +7293,24 @@ dependencies = [ "sp-core", ] +[[package]] +name = "sc-sync-state-rpc" +version = "0.8.0" +dependencies = [ + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "sc-chain-spec", + "sc-client-api", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-finality-grandpa", + "sc-rpc-api", + "serde_json", + "sp-blockchain", + "sp-runtime", +] + [[package]] name = "sc-telemetry" version = "2.0.0" @@ -7570,9 +7597,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" +checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4" dependencies = [ "itoa", "ryu", diff --git a/Cargo.toml b/Cargo.toml index ecea10cc45582..1d6235c0abcd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ members = [ "client/service", "client/service/test", "client/state-db", + "client/sync-state-rpc", "client/telemetry", "client/transaction-pool", "client/transaction-pool/graph", diff --git a/bin/node/cli/src/cli.rs b/bin/node/cli/src/cli.rs index 6e51dae93793f..2130ff1e4b106 100644 --- a/bin/node/cli/src/cli.rs +++ b/bin/node/cli/src/cli.rs @@ -59,9 +59,6 @@ pub enum Subcommand { /// Build a chain specification. BuildSpec(sc_cli::BuildSpecCmd), - /// Build a chain specification with a light client sync state. - BuildSyncSpec(sc_cli::BuildSyncSpecCmd), - /// Validate blocks. CheckBlock(sc_cli::CheckBlockCmd), diff --git a/bin/node/cli/src/command.rs b/bin/node/cli/src/command.rs index 139c25772286d..f8a0f3f9b3a34 100644 --- a/bin/node/cli/src/command.rs +++ b/bin/node/cli/src/command.rs @@ -21,7 +21,7 @@ use node_executor::Executor; use node_runtime::{Block, RuntimeApi}; use sc_cli::{Result, SubstrateCli, RuntimeVersion, Role, ChainSpec}; use sc_service::PartialComponents; -use crate::service::{new_partial, new_full_base, NewFullBase}; +use crate::service::new_partial; impl SubstrateCli for Cli { fn impl_name() -> String { @@ -102,17 +102,6 @@ pub fn run() -> Result<()> { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) }, - Some(Subcommand::BuildSyncSpec(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let chain_spec = config.chain_spec.cloned_box(); - let network_config = config.network.clone(); - let NewFullBase { task_manager, client, network_status_sinks, .. } - = new_full_base(config, |_, _| ())?; - - Ok((cmd.run(chain_spec, network_config, client, network_status_sinks), task_manager)) - }) - }, Some(Subcommand::CheckBlock(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 61b17899c6afa..adc5fe4fd58ff 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -122,12 +122,14 @@ pub fn new_partial(config: &Configuration) -> Result { @@ -94,6 +95,8 @@ pub struct FullDeps { pub pool: Arc

, /// The SelectChain Strategy pub select_chain: SC, + /// A copy of the chain spec. + pub chain_spec: Box, /// Whether to deny unsafe calls pub deny_unsafe: DenyUnsafe, /// BABE specific dependencies. @@ -109,9 +112,8 @@ pub type IoHandler = jsonrpc_core::IoHandler; pub fn create_full( deps: FullDeps, ) -> jsonrpc_core::IoHandler where - C: ProvideRuntimeApi, - C: HeaderBackend + HeaderMetadata + 'static, - C: Send + Sync + 'static, + C: ProvideRuntimeApi + HeaderBackend + AuxStore + + HeaderMetadata + Sync + Send + 'static, C::Api: substrate_frame_rpc_system::AccountNonceApi, C::Api: pallet_contracts_rpc::ContractsRuntimeApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, @@ -131,6 +133,7 @@ pub fn create_full( client, pool, select_chain, + chain_spec, deny_unsafe, babe, grandpa, @@ -164,8 +167,8 @@ pub fn create_full( io.extend_with( sc_consensus_babe_rpc::BabeApi::to_delegate( BabeRpcHandler::new( - client, - shared_epoch_changes, + client.clone(), + shared_epoch_changes.clone(), keystore, babe_config, select_chain, @@ -176,7 +179,7 @@ pub fn create_full( io.extend_with( sc_finality_grandpa_rpc::GrandpaApi::to_delegate( GrandpaRpcHandler::new( - shared_authority_set, + shared_authority_set.clone(), shared_voter_state, justification_stream, subscription_executor, @@ -185,6 +188,18 @@ pub fn create_full( ) ); + io.extend_with( + sc_sync_state_rpc::SyncStateRpcApi::to_delegate( + sc_sync_state_rpc::SyncStateRpcHandler::new( + chain_spec, + client, + shared_authority_set, + shared_epoch_changes, + deny_unsafe, + ) + ) + ); + io } diff --git a/client/chain-spec/Cargo.toml b/client/chain-spec/Cargo.toml index a73d809ba2523..79f14058aad6d 100644 --- a/client/chain-spec/Cargo.toml +++ b/client/chain-spec/Cargo.toml @@ -23,3 +23,7 @@ sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } sp-chain-spec = { version = "2.0.0", path = "../../primitives/chain-spec" } sc-telemetry = { version = "2.0.0", path = "../telemetry" } codec = { package = "parity-scale-codec", version = "1.3.4" } +sc-consensus-babe = { version = "0.8.0-rc6", path = "../consensus/babe" } +sp-consensus-babe = { version = "0.8.0-rc6", path = "../../primitives/consensus/babe" } +sc-consensus-epochs = { version = "0.8.0-rc6", path = "../consensus/epochs" } +sc-finality-grandpa = { version = "0.8.0-rc6", path = "../finality-grandpa" } diff --git a/client/chain-spec/src/chain_spec.rs b/client/chain-spec/src/chain_spec.rs index 1fbf0419e2001..39c47e32908df 100644 --- a/client/chain-spec/src/chain_spec.rs +++ b/client/chain-spec/src/chain_spec.rs @@ -27,7 +27,7 @@ use serde_json as json; use crate::{RuntimeGenesis, ChainType, extension::GetExtension, Properties}; use sc_network::config::MultiaddrWithPeerId; use sc_telemetry::TelemetryEndpoints; -use sp_runtime::traits::Block as BlockT; +use sp_runtime::traits::{Block as BlockT, NumberFor}; enum GenesisSource { File(PathBuf), @@ -264,7 +264,7 @@ impl ChainSpec { /// Hardcode infomation to allow light clients to sync quickly into the chain spec. fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState) { - self.client_spec.light_sync_state = Some(light_sync_state); + self.client_spec.light_sync_state = Some(light_sync_state); } } @@ -338,7 +338,7 @@ impl ChainSpec { impl crate::ChainSpec for ChainSpec where G: RuntimeGenesis + 'static, - E: GetExtension + serde::Serialize + Clone + Send + 'static, + E: GetExtension + serde::Serialize + Clone + Send + Sync + 'static, { fn boot_nodes(&self) -> &[MultiaddrWithPeerId] { ChainSpec::boot_nodes(self) @@ -400,7 +400,13 @@ where /// Hardcoded infomation that allows light clients to sync quickly. pub struct LightSyncState { /// The header of the best finalized block. - pub header: ::Header, + pub finalized_block_header: ::Header, + /// The epoch changes tree for babe. + pub babe_epoch_changes: sc_consensus_epochs::EpochChangesFor, + /// The babe weight of the finalized block. + pub babe_finalized_block_weight: sp_consensus_babe::BabeBlockWeight, + /// The authority set for grandpa. + pub grandpa_authority_set: sc_finality_grandpa::AuthoritySet<::Hash, NumberFor>, } impl LightSyncState { @@ -409,14 +415,26 @@ impl LightSyncState { use codec::Encode; SerializableLightSyncState { - header: StorageData(self.header.encode()), + finalized_block_header: StorageData(self.finalized_block_header.encode()), + babe_epoch_changes: + StorageData(self.babe_epoch_changes.encode()), + babe_finalized_block_weight: + self.babe_finalized_block_weight, + grandpa_authority_set: + StorageData(self.grandpa_authority_set.encode()), } } /// Convert from a `SerializableLightSyncState`. pub fn from_serializable(serialized: &SerializableLightSyncState) -> Result { Ok(Self { - header: codec::Decode::decode(&mut &serialized.header.0[..])?, + finalized_block_header: codec::Decode::decode(&mut &serialized.finalized_block_header.0[..])?, + babe_epoch_changes: + codec::Decode::decode(&mut &serialized.babe_epoch_changes.0[..])?, + babe_finalized_block_weight: + serialized.babe_finalized_block_weight, + grandpa_authority_set: + codec::Decode::decode(&mut &serialized.grandpa_authority_set.0[..])?, }) } } @@ -426,7 +444,10 @@ impl LightSyncState { #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub struct SerializableLightSyncState { - header: StorageData, + finalized_block_header: StorageData, + babe_epoch_changes: StorageData, + babe_finalized_block_weight: sp_consensus_babe::BabeBlockWeight, + grandpa_authority_set: StorageData, } #[cfg(test)] diff --git a/client/chain-spec/src/lib.rs b/client/chain-spec/src/lib.rs index f5afe496f1980..94ed93758bb2d 100644 --- a/client/chain-spec/src/lib.rs +++ b/client/chain-spec/src/lib.rs @@ -126,7 +126,7 @@ pub trait RuntimeGenesis: Serialize + DeserializeOwned + BuildStorage {} impl RuntimeGenesis for T {} /// Common interface of a chain specification. -pub trait ChainSpec: BuildStorage + Send { +pub trait ChainSpec: BuildStorage + Send + Sync { /// Spec name. fn name(&self) -> &str; /// Spec id. diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 09d5dcf094658..437799895779d 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -45,6 +45,9 @@ sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" sc-telemetry = { version = "2.0.0", path = "../telemetry" } substrate-prometheus-endpoint = { path = "../../utils/prometheus" , version = "0.8.0"} sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +sc-consensus-babe = { version = "0.8.0", path = "../consensus/babe" } +sc-consensus-epochs = { version = "0.8.0", path = "../consensus/epochs" } +sc-finality-grandpa = { version = "0.8.0", path = "../finality-grandpa" } names = "0.11.0" structopt = "0.3.8" sc-tracing = { version = "2.0.0", path = "../tracing" } diff --git a/client/cli/src/commands/build_sync_spec_cmd.rs b/client/cli/src/commands/build_sync_spec_cmd.rs deleted file mode 100644 index 3f1bfce6a3290..0000000000000 --- a/client/cli/src/commands/build_sync_spec_cmd.rs +++ /dev/null @@ -1,113 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2020 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 crate::error; -use crate::params::{SharedParams, NetworkParams}; -use crate::CliConfiguration; -use log::info; -use sc_network::config::build_multiaddr; -use sc_service::{config::{MultiaddrWithPeerId, NetworkConfiguration}, ChainSpec}; -use structopt::StructOpt; -use std::io::Write; -use std::sync::Arc; -use sp_runtime::traits::Block as BlockT; -use sc_service::chain_ops::build_light_sync_state; -use sc_service::NetworkStatusSinks; -use futures::{FutureExt, StreamExt}; -use futures::future::ready; - -/// The `build-sync-spec` command used to build a chain spec that contains a light client state -/// so that light clients can sync faster. -#[derive(Debug, StructOpt)] -pub struct BuildSyncSpecCmd { - /// Force raw genesis storage output. - #[structopt(long = "raw")] - pub raw: bool, - - /// Sync the chain using a full client first. - #[structopt(long)] - pub sync_first: bool, - - /// Disable adding the default bootnode to the specification. - /// - /// By default the `/ip4/127.0.0.1/tcp/30333/p2p/NODE_PEER_ID` bootnode is added to the - /// specification when no bootnode exists. - #[structopt(long = "disable-default-bootnode")] - pub disable_default_bootnode: bool, - - #[allow(missing_docs)] - #[structopt(flatten)] - pub shared_params: SharedParams, - - #[allow(missing_docs)] - #[structopt(flatten)] - pub network_params: NetworkParams, -} - -impl BuildSyncSpecCmd { - /// Run the build-sync-spec command - pub async fn run( - &self, - mut spec: Box, - network_config: NetworkConfiguration, - client: Arc, - network_status_sinks: NetworkStatusSinks, - ) -> error::Result<()> - where - B: BlockT, - CL: sp_blockchain::HeaderBackend, - { - if self.sync_first { - network_status_sinks.status_stream(std::time::Duration::from_secs(1)).filter(|status| { - ready(status.sync_state == sc_network::SyncState::Idle && status.num_sync_peers > 0) - }).into_future().map(drop).await; - } - - let light_sync_state = build_light_sync_state(client)?; - spec.set_light_sync_state(light_sync_state.to_serializable()); - - info!("Building chain spec"); - let raw_output = self.raw; - - if spec.boot_nodes().is_empty() && !self.disable_default_bootnode { - let keys = network_config.node_key.into_keypair()?; - let peer_id = keys.public().into_peer_id(); - let addr = MultiaddrWithPeerId { - multiaddr: build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(30333u16)], - peer_id, - }; - spec.add_boot_node(addr) - } - - let json = sc_service::chain_ops::build_spec(&*spec, raw_output)?; - if std::io::stdout().write_all(json.as_bytes()).is_err() { - let _ = std::io::stderr().write_all(b"Error writing to stdout\n"); - } - Ok(()) - } -} - -impl CliConfiguration for BuildSyncSpecCmd { - fn shared_params(&self) -> &SharedParams { - &self.shared_params - } - - fn network_params(&self) -> Option<&NetworkParams> { - Some(&self.network_params) - } -} diff --git a/client/cli/src/commands/mod.rs b/client/cli/src/commands/mod.rs index 899abf0c3d437..7b740d1003238 100644 --- a/client/cli/src/commands/mod.rs +++ b/client/cli/src/commands/mod.rs @@ -16,7 +16,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . mod build_spec_cmd; -mod build_sync_spec_cmd; mod check_block_cmd; mod export_blocks_cmd; mod export_state_cmd; @@ -37,7 +36,6 @@ pub mod utils; pub use self::{ build_spec_cmd::BuildSpecCmd, - build_sync_spec_cmd::BuildSyncSpecCmd, check_block_cmd::CheckBlockCmd, export_blocks_cmd::ExportBlocksCmd, export_state_cmd::ExportStateCmd, diff --git a/client/consensus/babe/src/aux_schema.rs b/client/consensus/babe/src/aux_schema.rs index 74078b4ee7b8a..287121566a417 100644 --- a/client/consensus/babe/src/aux_schema.rs +++ b/client/consensus/babe/src/aux_schema.rs @@ -126,7 +126,7 @@ pub(crate) fn write_block_weight( } /// Load the cumulative chain-weight associated with a block. -pub(crate) fn load_block_weight( +pub fn load_block_weight( backend: &B, block_hash: H, ) -> ClientResult> { diff --git a/client/finality-grandpa/src/authorities.rs b/client/finality-grandpa/src/authorities.rs index 7a064d7a6224b..57c30bc3b25c9 100644 --- a/client/finality-grandpa/src/authorities.rs +++ b/client/finality-grandpa/src/authorities.rs @@ -81,6 +81,11 @@ where N: Add + Ord + Clone + Debug, qed.", ) } + + /// Clone the inner `AuthoritySet`. + pub fn clone_inner(&self) -> AuthoritySet { + self.inner.read().clone() + } } impl From> for SharedAuthoritySet { @@ -101,7 +106,7 @@ pub(crate) struct Status { /// A set of authorities. #[derive(Debug, Clone, Encode, Decode, PartialEq)] -pub(crate) struct AuthoritySet { +pub struct AuthoritySet { /// The current active authorities. pub(crate) current_authorities: AuthorityList, /// The current set id. @@ -494,7 +499,7 @@ where /// Kinds of delays for pending changes. #[derive(Debug, Clone, Encode, Decode, PartialEq)] -pub(crate) enum DelayKind { +pub enum DelayKind { /// Depth in finalized chain. Finalized, /// Depth in best chain. The median last finalized block is calculated at the time the @@ -507,7 +512,7 @@ pub(crate) enum DelayKind { /// This will be applied when the announcing block is at some depth within /// the finalized or unfinalized chain. #[derive(Debug, Clone, Encode, PartialEq)] -pub(crate) struct PendingChange { +pub struct PendingChange { /// The new authorities and weights to apply. pub(crate) next_authorities: AuthorityList, /// How deep in the chain the announcing block must be diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 753fa5310d62e..8f351b66ecf52 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -124,7 +124,7 @@ mod observer; mod until_imported; mod voting_rule; -pub use authorities::SharedAuthoritySet; +pub use authorities::{SharedAuthoritySet, AuthoritySet}; pub use finality_proof::{FinalityProofFragment, FinalityProofProvider, StorageAndProofProvider}; pub use notification::{GrandpaJustificationSender, GrandpaJustificationStream}; pub use import::GrandpaBlockImport; diff --git a/client/service/src/chain_ops/build_sync_spec.rs b/client/service/src/chain_ops/build_sync_spec.rs deleted file mode 100644 index 9553ea21a6965..0000000000000 --- a/client/service/src/chain_ops/build_sync_spec.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use sp_runtime::traits::Block as BlockT; -use sp_blockchain::HeaderBackend; -use std::sync::Arc; -use sp_runtime::generic::BlockId; - -/// Build a `LightSyncState` from the CHT roots stored in a backend. -pub fn build_light_sync_state( - client: Arc, -) -> Result, sp_blockchain::Error> - where - TBl: BlockT, - TCl: HeaderBackend, -{ - let finalized_hash = client.info().finalized_hash; - let header = client.header(BlockId::Hash(finalized_hash))?.unwrap(); - - Ok(sc_chain_spec::LightSyncState { - header - }) -} diff --git a/client/service/src/chain_ops/mod.rs b/client/service/src/chain_ops/mod.rs index e6b2fdfb8e0e6..af6e6f632fc06 100644 --- a/client/service/src/chain_ops/mod.rs +++ b/client/service/src/chain_ops/mod.rs @@ -21,11 +21,9 @@ mod export_blocks; mod export_raw_state; mod import_blocks; mod revert_chain; -mod build_sync_spec; pub use check_block::*; pub use export_blocks::*; pub use export_raw_state::*; pub use import_blocks::*; pub use revert_chain::*; -pub use build_sync_spec::*; diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index cfe815f174fac..7c4e28f4ddc4a 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -194,7 +194,7 @@ where F: Clone + Send + 'static, L: Clone + Send +'static, U: Clone + Send + 'st } } -fn node_config ( +fn node_config ( index: usize, spec: &GenericChainSpec, role: Role, @@ -275,7 +275,7 @@ fn node_config TestNet where F: TestNetNode, L: TestNetNode, - E: ChainSpecExtension + Clone + 'static + Send, + E: ChainSpecExtension + Clone + 'static + Send + Sync, G: RuntimeGenesis + 'static, { fn new( @@ -389,7 +389,7 @@ pub fn connectivity( full_builder: Fb, light_builder: Lb, ) where - E: ChainSpecExtension + Clone + 'static + Send, + E: ChainSpecExtension + Clone + 'static + Send + Sync, G: RuntimeGenesis + 'static, Fb: Fn(Configuration) -> Result, F: TestNetNode, @@ -509,7 +509,7 @@ pub fn sync( B: FnMut(&F, &mut U), ExF: FnMut(&F, &U) -> ::Extrinsic, U: Clone + Send + 'static, - E: ChainSpecExtension + Clone + 'static + Send, + E: ChainSpecExtension + Clone + 'static + Send + Sync, G: RuntimeGenesis + 'static, { const NUM_FULL_NODES: usize = 10; @@ -584,7 +584,7 @@ pub fn consensus( F: TestNetNode, Lb: Fn(Configuration) -> Result, L: TestNetNode, - E: ChainSpecExtension + Clone + 'static + Send, + E: ChainSpecExtension + Clone + 'static + Send + Sync, G: RuntimeGenesis + 'static, { const NUM_FULL_NODES: usize = 10; diff --git a/client/sync-state-rpc/Cargo.toml b/client/sync-state-rpc/Cargo.toml new file mode 100644 index 0000000000000..8da372db94ffc --- /dev/null +++ b/client/sync-state-rpc/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "sc-sync-state-rpc" +version = "0.8.0" +authors = ["Parity Technologies "] +description = "A RPC handler to create sync states for light clients." +edition = "2018" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +jsonrpc-core = "15.0" +jsonrpc-core-client = "15.0" +jsonrpc-derive = "15.0" +sc-chain-spec = { version = "2.0.0", path = "../chain-spec" } +sc-client-api = { version = "2.0.0", path = "../api" } +sc-consensus-babe = { version = "0.8.0", path = "../consensus/babe" } +sc-consensus-epochs = { version = "0.8.0", path = "../consensus/epochs" } +sc-finality-grandpa = { version = "0.8.0", path = "../finality-grandpa" } +sc-rpc-api = { version = "0.8.0", path = "../rpc-api" } +serde_json = "1.0.58" +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } diff --git a/client/sync-state-rpc/src/lib.rs b/client/sync-state-rpc/src/lib.rs new file mode 100644 index 0000000000000..fa433e5e31d2d --- /dev/null +++ b/client/sync-state-rpc/src/lib.rs @@ -0,0 +1,128 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! A RPC handler to create sync states for light clients. +//! Currently only usable with BABE + GRANDPA. + +use sp_runtime::traits::{Block as BlockT, NumberFor}; +use sp_blockchain::HeaderBackend; +use std::sync::Arc; +use sp_runtime::generic::BlockId; + +use jsonrpc_derive::rpc; + +type SharedAuthoritySet = + sc_finality_grandpa::SharedAuthoritySet<::Hash, NumberFor>; +type SharedEpochChanges = sc_consensus_epochs::SharedEpochChanges; + +struct Error(sp_blockchain::Error); + +impl From for jsonrpc_core::Error { + fn from(error: Error) -> Self { + jsonrpc_core::Error { + message: error.0.to_string(), + code: jsonrpc_core::ErrorCode::ServerError(1), + data: None, + } + } +} + +/// An api for sync state RPC calls. +#[rpc] +pub trait SyncStateRpcApi { + /// Returns the json-serialized chainspec running the node, with a sync state. + #[rpc(name = "sync_state_genSyncSpec", returns = "jsonrpc_core::Value")] + fn system_gen_sync_spec(&self, raw: bool) + -> jsonrpc_core::Result; +} + +/// The handler for sync state RPC calls. +pub struct SyncStateRpcHandler { + chain_spec: Box, + client: Arc, + shared_authority_set: SharedAuthoritySet, + shared_epoch_changes: SharedEpochChanges, + deny_unsafe: sc_rpc_api::DenyUnsafe, +} + +impl SyncStateRpcHandler + where + TBl: BlockT, + TCl: HeaderBackend + sc_client_api::AuxStore + 'static, +{ + /// Create a new handler. + pub fn new( + chain_spec: Box, + client: Arc, + shared_authority_set: SharedAuthoritySet, + shared_epoch_changes: SharedEpochChanges, + deny_unsafe: sc_rpc_api::DenyUnsafe, + ) -> Self { + Self { + chain_spec, client, shared_authority_set, shared_epoch_changes, deny_unsafe, + } + } + + fn build_sync_state(&self) -> Result, sp_blockchain::Error> { + let finalized_hash = self.client.info().finalized_hash; + let finalized_header = self.client.header(BlockId::Hash(finalized_hash))? + .ok_or_else(|| sp_blockchain::Error::Msg( + format!("Failed to get the header for block {:?}", finalized_hash) + ))?; + + let finalized_block_weight = sc_consensus_babe::aux_schema::load_block_weight( + &*self.client, + finalized_hash, + )? + .ok_or_else(|| sp_blockchain::Error::Msg( + format!("Failed to load the block weight for block {:?}", finalized_hash) + ))?; + + Ok(sc_chain_spec::LightSyncState { + finalized_block_header: finalized_header, + babe_epoch_changes: self.shared_epoch_changes.lock().clone(), + babe_finalized_block_weight: finalized_block_weight, + grandpa_authority_set: self.shared_authority_set.clone_inner(), + }) + } +} + +impl SyncStateRpcApi for SyncStateRpcHandler + where + TBl: BlockT, + TCl: HeaderBackend + sc_client_api::AuxStore + 'static, +{ + fn system_gen_sync_spec(&self, raw: bool) + -> jsonrpc_core::Result + { + if let Err(err) = self.deny_unsafe.check_if_safe() { + return Err(err.into()); + } + + let mut chain_spec = self.chain_spec.cloned_box(); + + let sync_state = self.build_sync_state().map_err(Error)?; + + chain_spec.set_light_sync_state(sync_state.to_serializable()); + let string = chain_spec.as_json(raw).map_err(map_error)?; + + serde_json::from_str(&string).map_err(|err| map_error(err.to_string())) + } +} + +fn map_error(error: String) -> jsonrpc_core::Error { + Error(sp_blockchain::Error::Msg(error)).into() +} diff --git a/utils/browser/src/lib.rs b/utils/browser/src/lib.rs index ffd0a134be19e..196282ddecab7 100644 --- a/utils/browser/src/lib.rs +++ b/utils/browser/src/lib.rs @@ -41,7 +41,7 @@ pub async fn browser_configuration(chain_spec: GenericChainSpec) -> Result> where G: RuntimeGenesis + 'static, - E: Extension + 'static + Send, + E: Extension + 'static + Send + Sync, { let name = chain_spec.name().to_string();