From 6e4d30e8ad6288a62251b0160f091a98b9110007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 4 Aug 2021 15:38:14 +0200 Subject: [PATCH] Decouples light-sync state from chain spec (#9491) * Decouples light-sync state from chain spec This decouples the light-sync state from chain spec. First, the light-sync state currently only works with BABE+Grandpa, so not all *Substrate* based chains can use this feature. The next problem was also that this pulled the `sc-consensus-babe` and `sc-finality-grandpa` crate into `sc-chain-spec`. If a chain now wants to support the light-sync state, it needs to add the `LightSyncStateExtension` to the chain spec as an extension. This is documented in the crate level docs of `sc-sync-state-rpc`. If this extension is not available, `SyncStateRpc` fails at initialization. * Fix compilation for browser * Fmt --- substrate/Cargo.lock | 7 +- .../bin/node-template/node/src/service.rs | 4 +- substrate/bin/node/cli/Cargo.toml | 1 + substrate/bin/node/cli/src/chain_spec.rs | 2 + substrate/bin/node/cli/src/service.rs | 7 +- substrate/bin/node/rpc/src/lib.rs | 6 +- substrate/client/chain-spec/Cargo.toml | 4 - .../client/chain-spec/derive/src/impls.rs | 9 ++ substrate/client/chain-spec/src/chain_spec.rs | 81 ++---------- substrate/client/chain-spec/src/extension.rs | 44 +++++-- substrate/client/chain-spec/src/lib.rs | 12 +- substrate/client/consensus/babe/src/lib.rs | 2 +- substrate/client/service/src/builder.rs | 20 +-- substrate/client/service/src/lib.rs | 34 +++-- substrate/client/sync-state-rpc/Cargo.toml | 2 + substrate/client/sync-state-rpc/src/lib.rs | 123 ++++++++++++++---- .../test-utils/test-runner/src/client.rs | 2 +- 17 files changed, 211 insertions(+), 149 deletions(-) diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 11359c0781..cc6299397b 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -4222,6 +4222,7 @@ dependencies = [ "sc-rpc", "sc-service", "sc-service-test", + "sc-sync-state-rpc", "sc-telemetry", "sc-tracing", "sc-transaction-pool", @@ -7126,14 +7127,10 @@ 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-consensus-babe", "sp-core", "sp-runtime", ] @@ -8151,12 +8148,14 @@ dependencies = [ "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", + "parity-scale-codec", "sc-chain-spec", "sc-client-api", "sc-consensus-babe", "sc-consensus-epochs", "sc-finality-grandpa", "sc-rpc-api", + "serde", "serde_json", "sp-blockchain", "sp-runtime", diff --git a/substrate/bin/node-template/node/src/service.rs b/substrate/bin/node-template/node/src/service.rs index 9eba1d0e9e..8eef2ce090 100644 --- a/substrate/bin/node-template/node/src/service.rs +++ b/substrate/bin/node-template/node/src/service.rs @@ -203,7 +203,7 @@ pub fn new_full(mut config: Configuration) -> Result let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; - crate::rpc::create_full(deps) + Ok(crate::rpc::create_full(deps)) }) }; @@ -436,7 +436,7 @@ pub fn new_light(mut config: Configuration) -> Result transaction_pool, task_manager: &mut task_manager, on_demand: Some(on_demand), - rpc_extensions_builder: Box::new(|_, _| ()), + rpc_extensions_builder: Box::new(|_, _| Ok(())), config, client, keystore: keystore_container.sync_keystore(), diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 12a76cf323..21c42a5ed2 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -77,6 +77,7 @@ sc-service = { version = "0.10.0-dev", default-features = false, path = "../../. sc-tracing = { version = "4.0.0-dev", path = "../../../client/tracing" } sc-telemetry = { version = "4.0.0-dev", path = "../../../client/telemetry" } sc-authority-discovery = { version = "0.10.0-dev", path = "../../../client/authority-discovery" } +sc-sync-state-rpc = { version = "0.10.0-dev", path = "../../../client/sync-state-rpc" } # frame dependencies pallet-indices = { version = "4.0.0-dev", path = "../../../frame/indices" } diff --git a/substrate/bin/node/cli/src/chain_spec.rs b/substrate/bin/node/cli/src/chain_spec.rs index 2891736e5c..5d9e049cc3 100644 --- a/substrate/bin/node/cli/src/chain_spec.rs +++ b/substrate/bin/node/cli/src/chain_spec.rs @@ -57,6 +57,8 @@ pub struct Extensions { pub fork_blocks: sc_client_api::ForkBlocks, /// Known bad block hashes. pub bad_blocks: sc_client_api::BadBlocks, + /// The light sync state extension used by the sync-state rpc. + pub light_sync_state: sc_sync_state_rpc::LightSyncStateExtension, } /// Specialized `ChainSpec`. diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 301df01c55..1bd1e67485 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -49,7 +49,10 @@ pub fn new_partial( sc_consensus::DefaultImportQueue, sc_transaction_pool::FullPool, ( - impl Fn(node_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor) -> node_rpc::IoHandler, + impl Fn( + node_rpc::DenyUnsafe, + sc_rpc::SubscriptionTaskExecutor, + ) -> Result, ( sc_consensus_babe::BabeBlockImport, grandpa::LinkHalf, @@ -180,7 +183,7 @@ pub fn new_partial( }, }; - node_rpc::create_full(deps) + node_rpc::create_full(deps).map_err(Into::into) }; (rpc_extensions_builder, rpc_setup) diff --git a/substrate/bin/node/rpc/src/lib.rs b/substrate/bin/node/rpc/src/lib.rs index 1b326eda6c..2f7862d3d2 100644 --- a/substrate/bin/node/rpc/src/lib.rs +++ b/substrate/bin/node/rpc/src/lib.rs @@ -111,7 +111,7 @@ pub type IoHandler = jsonrpc_core::IoHandler; /// Instantiate all Full RPC extensions. pub fn create_full( deps: FullDeps, -) -> jsonrpc_core::IoHandler +) -> Result, Box> where C: ProvideRuntimeApi + HeaderBackend @@ -178,10 +178,10 @@ where shared_authority_set, shared_epoch_changes, deny_unsafe, - ), + )?, )); - io + Ok(io) } /// Instantiate all Light RPC extensions. diff --git a/substrate/client/chain-spec/Cargo.toml b/substrate/client/chain-spec/Cargo.toml index fcc5bc3bda..3243430989 100644 --- a/substrate/client/chain-spec/Cargo.toml +++ b/substrate/client/chain-spec/Cargo.toml @@ -22,7 +22,3 @@ serde_json = "1.0.41" sp-runtime = { version = "4.0.0-dev", path = "../../primitives/runtime" } sc-telemetry = { version = "4.0.0-dev", path = "../telemetry" } codec = { package = "parity-scale-codec", version = "2.0.0" } -sc-consensus-babe = { version = "0.10.0-dev", path = "../consensus/babe" } -sp-consensus-babe = { version = "0.10.0-dev", path = "../../primitives/consensus/babe" } -sc-consensus-epochs = { version = "0.10.0-dev", path = "../consensus/epochs" } -sc-finality-grandpa = { version = "0.10.0-dev", path = "../finality-grandpa" } diff --git a/substrate/client/chain-spec/derive/src/impls.rs b/substrate/client/chain-spec/derive/src/impls.rs index 23415903b4..8c56430e81 100644 --- a/substrate/client/chain-spec/derive/src/impls.rs +++ b/substrate/client/chain-spec/derive/src/impls.rs @@ -65,6 +65,15 @@ pub fn extension_derive(ast: &DeriveInput) -> proc_macro::TokenStream { _ => self, } } + + fn get_any_mut(&mut self, t: std::any::TypeId) -> &mut dyn std::any::Any { + use std::any::{Any, TypeId}; + + match t { + #( x if x == TypeId::of::<#field_types>() => &mut self.#field_names ),*, + _ => self, + } + } } } }) diff --git a/substrate/client/chain-spec/src/chain_spec.rs b/substrate/client/chain-spec/src/chain_spec.rs index 681ab8ea64..fcdb053c47 100644 --- a/substrate/client/chain-spec/src/chain_spec.rs +++ b/substrate/client/chain-spec/src/chain_spec.rs @@ -28,10 +28,7 @@ use sp_core::{ storage::{ChildInfo, Storage, StorageChild, StorageData, StorageKey}, Bytes, }; -use sp_runtime::{ - traits::{Block as BlockT, NumberFor}, - BuildStorage, -}; +use sp_runtime::BuildStorage; use std::{borrow::Cow, collections::HashMap, fs::File, path::PathBuf, sync::Arc}; enum GenesisSource { @@ -167,7 +164,6 @@ struct ClientSpec { consensus_engine: (), #[serde(skip_serializing)] genesis: serde::de::IgnoredAny, - light_sync_state: Option, /// Mapping from `block_hash` to `wasm_code`. /// /// The given `wasm_code` will be used to substitute the on-chain wasm code from the given @@ -231,11 +227,16 @@ impl ChainSpec { self.client_spec.boot_nodes.push(addr) } - /// Returns a reference to defined chain spec extensions. + /// Returns a reference to the defined chain spec extensions. pub fn extensions(&self) -> &E { &self.client_spec.extensions } + /// Returns a mutable reference to the defined chain spec extensions. + pub fn extensions_mut(&mut self) -> &mut E { + &mut self.client_spec.extensions + } + /// Create hardcoded spec. pub fn from_genesis G + 'static + Send + Sync>( name: &str, @@ -259,7 +260,6 @@ impl ChainSpec { extensions, consensus_engine: (), genesis: Default::default(), - light_sync_state: None, code_substitutes: HashMap::new(), }; @@ -270,11 +270,6 @@ impl ChainSpec { fn chain_type(&self) -> ChainType { self.client_spec.chain_type.clone() } - - /// 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); - } } impl ChainSpec { @@ -379,6 +374,10 @@ where ChainSpec::extensions(self) as &dyn GetExtension } + fn extensions_mut(&mut self) -> &mut dyn GetExtension { + ChainSpec::extensions_mut(self) as &mut dyn GetExtension + } + fn as_json(&self, raw: bool) -> Result { ChainSpec::as_json(self, raw) } @@ -395,10 +394,6 @@ where self.genesis = GenesisSource::Storage(storage); } - fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState) { - ChainSpec::set_light_sync_state(self, light_sync_state) - } - fn code_substitutes(&self) -> std::collections::HashMap> { self.client_spec .code_substitutes @@ -408,60 +403,6 @@ where } } -/// Hardcoded infomation that allows light clients to sync quickly. -pub struct LightSyncState { - /// The header of the best finalized block. - 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 { - /// Convert into a `SerializableLightSyncState`. - pub fn to_serializable(&self) -> SerializableLightSyncState { - use codec::Encode; - - SerializableLightSyncState { - 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 { - 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[..], - )?, - }) - } -} - -/// The serializable form of `LightSyncState`. Created using `LightSyncState::serialize`. -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct SerializableLightSyncState { - finalized_block_header: StorageData, - babe_epoch_changes: StorageData, - babe_finalized_block_weight: sp_consensus_babe::BabeBlockWeight, - grandpa_authority_set: StorageData, -} - #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/chain-spec/src/extension.rs b/substrate/client/chain-spec/src/extension.rs index 665f51303b..4b59232cf5 100644 --- a/substrate/client/chain-spec/src/extension.rs +++ b/substrate/client/chain-spec/src/extension.rs @@ -126,8 +126,10 @@ pub trait Extension: Serialize + DeserializeOwned + Clone { /// Get an extension of specific type. fn get(&self) -> Option<&T>; - /// Get an extension of specific type as refernce to `Any` + /// Get an extension of specific type as reference to `Any`. fn get_any(&self, t: TypeId) -> &dyn Any; + /// Get an extension of specific type as mutable reference to `Any`. + fn get_any_mut(&mut self, t: TypeId) -> &mut dyn Any; /// Get forkable extensions of specific type. fn forks(&self) -> Option> @@ -151,6 +153,9 @@ impl Extension for crate::NoExtension { fn get_any(&self, _t: TypeId) -> &dyn Any { self } + fn get_any_mut(&mut self, _: TypeId) -> &mut dyn Any { + self + } } pub trait IsForks { @@ -240,16 +245,26 @@ where type Forks = Self; fn get(&self) -> Option<&T> { - match TypeId::of::() { - x if x == TypeId::of::() => ::downcast_ref(&self.base), - _ => self.base.get(), + if TypeId::of::() == TypeId::of::() { + ::downcast_ref(&self.base) + } else { + self.base.get() } } fn get_any(&self, t: TypeId) -> &dyn Any { - match t { - x if x == TypeId::of::() => &self.base, - _ => self.base.get_any(t), + if t == TypeId::of::() { + &self.base + } else { + self.base.get_any(t) + } + } + + fn get_any_mut(&mut self, t: TypeId) -> &mut dyn Any { + if t == TypeId::of::() { + &mut self.base + } else { + self.base.get_any_mut(t) } } @@ -273,20 +288,31 @@ where pub trait GetExtension { /// Get an extension of specific type. fn get_any(&self, t: TypeId) -> &dyn Any; + + /// Get an extension of specific type with mutable access. + fn get_any_mut(&mut self, t: TypeId) -> &mut dyn Any; } impl GetExtension for E { fn get_any(&self, t: TypeId) -> &dyn Any { Extension::get_any(self, t) } + + fn get_any_mut(&mut self, t: TypeId) -> &mut dyn Any { + Extension::get_any_mut(self, t) + } } -/// Helper function that queries an extension by type from `GetExtension` -/// trait object. +/// Helper function that queries an extension by type from `GetExtension` trait object. pub fn get_extension(e: &dyn GetExtension) -> Option<&T> { ::downcast_ref(GetExtension::get_any(e, TypeId::of::())) } +/// Helper function that queries an extension by type from `GetExtension` trait object. +pub fn get_extension_mut(e: &mut dyn GetExtension) -> Option<&mut T> { + ::downcast_mut(GetExtension::get_any_mut(e, TypeId::of::())) +} + #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index ac580802a5..334d8f8b3d 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -110,10 +110,10 @@ mod chain_spec; mod extension; -pub use chain_spec::{ - ChainSpec as GenericChainSpec, LightSyncState, NoExtension, SerializableLightSyncState, +pub use chain_spec::{ChainSpec as GenericChainSpec, NoExtension}; +pub use extension::{ + get_extension, get_extension_mut, Extension, Fork, Forks, GetExtension, Group, }; -pub use extension::{get_extension, Extension, Fork, Forks, GetExtension, Group}; pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; use sc_network::config::MultiaddrWithPeerId; @@ -169,8 +169,10 @@ pub trait ChainSpec: BuildStorage + Send + Sync { /// /// Returns an empty JSON object if 'properties' not defined in config fn properties(&self) -> Properties; - /// Returns a reference to defined chain spec extensions. + /// Returns a reference to the defined chain spec extensions. fn extensions(&self) -> &dyn GetExtension; + /// Returns a mutable reference to the defined chain spec extensions. + fn extensions_mut(&mut self) -> &mut dyn GetExtension; /// Add a bootnode to the list. fn add_boot_node(&mut self, addr: MultiaddrWithPeerId); /// Return spec as JSON. @@ -183,8 +185,6 @@ pub trait ChainSpec: BuildStorage + Send + Sync { /// /// This will be used as storage at genesis. fn set_storage(&mut self, storage: Storage); - /// Hardcode infomation to allow light clients to sync quickly into the chain spec. - fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState); /// Returns code substitutes that should be used for the on chain wasm. fn code_substitutes(&self) -> std::collections::HashMap>; } diff --git a/substrate/client/consensus/babe/src/lib.rs b/substrate/client/consensus/babe/src/lib.rs index 2828c345c4..d5caf36542 100644 --- a/substrate/client/consensus/babe/src/lib.rs +++ b/substrate/client/consensus/babe/src/lib.rs @@ -127,7 +127,7 @@ pub use sp_consensus_babe::{ CompatibleDigestItem, NextConfigDescriptor, NextEpochDescriptor, PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, }, - AuthorityId, AuthorityPair, AuthoritySignature, BabeApi, BabeAuthorityWeight, + AuthorityId, AuthorityPair, AuthoritySignature, BabeApi, BabeAuthorityWeight, BabeBlockWeight, BabeEpochConfiguration, BabeGenesisConfiguration, ConsensusLog, BABE_ENGINE_ID, VRF_OUTPUT_LENGTH, }; diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index fb24a89013..83c8e1d9d1 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -80,12 +80,12 @@ pub trait RpcExtensionBuilder { &self, deny: sc_rpc::DenyUnsafe, subscription_executor: sc_rpc::SubscriptionTaskExecutor, - ) -> Self::Output; + ) -> Result; } impl RpcExtensionBuilder for F where - F: Fn(sc_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor) -> R, + F: Fn(sc_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor) -> Result, R: sc_rpc::RpcExtension, { type Output = R; @@ -94,7 +94,7 @@ where &self, deny: sc_rpc::DenyUnsafe, subscription_executor: sc_rpc::SubscriptionTaskExecutor, - ) -> Self::Output { + ) -> Result { (*self)(deny, subscription_executor) } } @@ -114,8 +114,8 @@ where &self, _deny: sc_rpc::DenyUnsafe, _subscription_executor: sc_rpc::SubscriptionTaskExecutor, - ) -> Self::Output { - self.0.clone() + ) -> Result { + Ok(self.0.clone()) } } @@ -655,7 +655,7 @@ where gen_handler( sc_rpc::DenyUnsafe::No, sc_rpc_server::RpcMiddleware::new(rpc_metrics, "inbrowser"), - ) + )? .into(), )); @@ -742,7 +742,7 @@ fn gen_handler( rpc_extensions_builder: &(dyn RpcExtensionBuilder + Send), offchain_storage: Option<>::OffchainStorage>, system_rpc_tx: TracingUnboundedSender>, -) -> sc_rpc_server::RpcHandler +) -> Result, Error> where TBl: BlockT, TCl: ProvideRuntimeApi @@ -813,7 +813,7 @@ where offchain::OffchainApi::to_delegate(offchain) }); - sc_rpc_server::rpc_handler( + Ok(sc_rpc_server::rpc_handler( ( state::StateApi::to_delegate(state), state::ChildStateApi::to_delegate(child_state), @@ -821,10 +821,10 @@ where maybe_offchain_rpc, author::AuthorApi::to_delegate(author), system::SystemApi::to_delegate(system), - rpc_extensions_builder.build(deny_unsafe, task_executor), + rpc_extensions_builder.build(deny_unsafe, task_executor)?, ), rpc_middleware, - ) + )) } /// Parameters to pass into `build_network`. diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index a6cefcd5db..5791165e53 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -348,28 +348,31 @@ fn start_rpc_servers< H: FnMut( sc_rpc::DenyUnsafe, sc_rpc_server::RpcMiddleware, - ) -> sc_rpc_server::RpcHandler, + ) -> Result, Error>, >( config: &Configuration, mut gen_handler: H, rpc_metrics: sc_rpc_server::RpcMetrics, -) -> Result, error::Error> { +) -> Result, Error> { fn maybe_start_server( address: Option, mut start: F, - ) -> Result, io::Error> + ) -> Result, Error> where - F: FnMut(&SocketAddr) -> Result, + F: FnMut(&SocketAddr) -> Result, { address .map(|mut address| { - start(&address).or_else(|e| match e.kind() { - io::ErrorKind::AddrInUse | io::ErrorKind::PermissionDenied => { - warn!("Unable to bind RPC server to {}. Trying random port.", address); - address.set_port(0); - start(&address) + start(&address).or_else(|e| match e { + Error::Io(e) => match e.kind() { + io::ErrorKind::AddrInUse | io::ErrorKind::PermissionDenied => { + warn!("Unable to bind RPC server to {}. Trying random port.", address); + address.set_port(0); + start(&address) + }, + _ => Err(e.into()), }, - _ => Err(e), + e => Err(e), }) }) .transpose() @@ -390,8 +393,9 @@ fn start_rpc_servers< gen_handler( sc_rpc::DenyUnsafe::No, sc_rpc_server::RpcMiddleware::new(rpc_metrics.clone(), "ipc"), - ), + )?, ) + .map_err(Error::from) }), maybe_start_server(config.rpc_http, |address| { sc_rpc_server::start_http( @@ -401,9 +405,10 @@ fn start_rpc_servers< gen_handler( deny_unsafe(&address, &config.rpc_methods), sc_rpc_server::RpcMiddleware::new(rpc_metrics.clone(), "http"), - ), + )?, config.rpc_max_payload, ) + .map_err(Error::from) })? .map(|s| waiting::HttpServer(Some(s))), maybe_start_server(config.rpc_ws, |address| { @@ -414,9 +419,10 @@ fn start_rpc_servers< gen_handler( deny_unsafe(&address, &config.rpc_methods), sc_rpc_server::RpcMiddleware::new(rpc_metrics.clone(), "ws"), - ), + )?, config.rpc_max_payload, ) + .map_err(Error::from) })? .map(|s| waiting::WsServer(Some(s))), ))) @@ -428,7 +434,7 @@ fn start_rpc_servers< H: FnMut( sc_rpc::DenyUnsafe, sc_rpc_server::RpcMiddleware, - ) -> sc_rpc_server::RpcHandler, + ) -> Result, Error>, >( _: &Configuration, _: H, diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml index 0402d16ae0..a96b80ff93 100644 --- a/substrate/client/sync-state-rpc/Cargo.toml +++ b/substrate/client/sync-state-rpc/Cargo.toml @@ -24,5 +24,7 @@ sc-consensus-epochs = { version = "0.10.0-dev", path = "../consensus/epochs" } sc-finality-grandpa = { version = "0.10.0-dev", path = "../finality-grandpa" } sc-rpc-api = { version = "0.10.0-dev", path = "../rpc-api" } serde_json = "1.0.58" +serde = { version = "1.0.126", features = ["derive"] } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-runtime = { version = "4.0.0-dev", path = "../../primitives/runtime" } +codec = { package = "parity-scale-codec", version = "2.0.0" } diff --git a/substrate/client/sync-state-rpc/src/lib.rs b/substrate/client/sync-state-rpc/src/lib.rs index e786a10cd4..a1621e3986 100644 --- a/substrate/client/sync-state-rpc/src/lib.rs +++ b/substrate/client/sync-state-rpc/src/lib.rs @@ -17,10 +17,31 @@ // along with this program. If not, see . //! A RPC handler to create sync states for light clients. +//! //! Currently only usable with BABE + GRANDPA. +//! +//! # Usage +//! +//! To use the light sync state, it needs to be added as an extension to the chain spec: +//! +//! ``` +//! use sc_sync_state_rpc::LightSyncStateExtension; +//! +//! #[derive(Default, Clone, serde::Serialize, serde::Deserialize, sc_chain_spec::ChainSpecExtension)] +//! #[serde(rename_all = "camelCase")] +//! pub struct Extensions { +//! light_sync_state: LightSyncStateExtension, +//! } +//! +//! type ChainSpec = sc_chain_spec::GenericChainSpec<(), Extensions>; +//! ``` +//! +//! If the [`LightSyncStateExtension`] is not added as an extension to the chain spec, +//! the [`SyncStateRpcHandler`] will fail at instantiation. #![deny(unused_crate_dependencies)] +use sc_client_api::StorageData; use sp_blockchain::HeaderBackend; use sp_runtime::{ generic::BlockId, @@ -35,17 +56,24 @@ type SharedAuthoritySet = type SharedEpochChanges = sc_consensus_epochs::SharedEpochChanges; +/// Error type used by this crate. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] -enum Error { +pub enum Error { #[error(transparent)] Blockchain(#[from] sp_blockchain::Error), #[error("Failed to load the block weight for block {0:?}")] - LoadingBlockWeightFailed(::Hash), + LoadingBlockWeightFailed(Block::Hash), #[error("JsonRpc error: {0}")] JsonRpc(String), + + #[error( + "The light sync state extension is not provided by the chain spec. \ + Read the `sc-sync-state-rpc` crate docs on how to do this!" + )] + LightSyncStateExtensionNotFound, } impl From> for jsonrpc_core::Error { @@ -58,6 +86,40 @@ impl From> for jsonrpc_core::Error { } } +/// Serialize the given `val` by encoding it with SCALE codec and serializing it as hex. +fn serialize_encoded( + val: &T, + s: S, +) -> Result { + let encoded = StorageData(val.encode()); + serde::Serialize::serialize(&encoded, s) +} + +/// The light sync state extension. +/// +/// This represents a JSON serialized [`LightSyncState`]. It is required to be added to the +/// chain-spec as an extension. +pub type LightSyncStateExtension = Option; + +/// Hardcoded infomation that allows light clients to sync quickly. +#[derive(serde::Serialize, Clone)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct LightSyncState { + /// The header of the best finalized block. + #[serde(serialize_with = "serialize_encoded")] + pub finalized_block_header: ::Header, + /// The epoch changes tree for babe. + #[serde(serialize_with = "serialize_encoded")] + pub babe_epoch_changes: sc_consensus_epochs::EpochChangesFor, + /// The babe weight of the finalized block. + pub babe_finalized_block_weight: sc_consensus_babe::BabeBlockWeight, + /// The authority set for grandpa. + #[serde(serialize_with = "serialize_encoded")] + pub grandpa_authority_set: + sc_finality_grandpa::AuthoritySet<::Hash, NumberFor>, +} + /// An api for sync state RPC calls. #[rpc] pub trait SyncStateRpcApi { @@ -67,31 +129,37 @@ pub trait SyncStateRpcApi { } /// The handler for sync state RPC calls. -pub struct SyncStateRpcHandler { +pub struct SyncStateRpcHandler { chain_spec: Box, - client: Arc, - shared_authority_set: SharedAuthoritySet, - shared_epoch_changes: SharedEpochChanges, + client: Arc, + shared_authority_set: SharedAuthoritySet, + shared_epoch_changes: SharedEpochChanges, deny_unsafe: sc_rpc_api::DenyUnsafe, } -impl SyncStateRpcHandler +impl SyncStateRpcHandler where - TBl: BlockT, - TCl: HeaderBackend + sc_client_api::AuxStore + 'static, + Block: BlockT, + Backend: 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, + 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 } + ) -> Result> { + if sc_chain_spec::get_extension::(chain_spec.extensions()) + .is_some() + { + Ok(Self { chain_spec, client, shared_authority_set, shared_epoch_changes, deny_unsafe }) + } else { + Err(Error::::LightSyncStateExtensionNotFound) + } } - fn build_sync_state(&self) -> Result, Error> { + fn build_sync_state(&self) -> Result, Error> { let finalized_hash = self.client.info().finalized_hash; let finalized_header = self .client @@ -102,7 +170,7 @@ where sc_consensus_babe::aux_schema::load_block_weight(&*self.client, finalized_hash)? .ok_or_else(|| Error::LoadingBlockWeightFailed(finalized_hash))?; - Ok(sc_chain_spec::LightSyncState { + Ok(LightSyncState { finalized_block_header: finalized_header, babe_epoch_changes: self.shared_epoch_changes.shared_data().clone(), babe_finalized_block_weight: finalized_block_weight, @@ -111,10 +179,10 @@ where } } -impl SyncStateRpcApi for SyncStateRpcHandler +impl SyncStateRpcApi for SyncStateRpcHandler where - TBl: BlockT, - TCl: HeaderBackend + sc_client_api::AuxStore + 'static, + Block: BlockT, + Backend: 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() { @@ -123,12 +191,21 @@ where let mut chain_spec = self.chain_spec.cloned_box(); - let sync_state = self.build_sync_state().map_err(map_error::>)?; + let sync_state = self.build_sync_state().map_err(map_error::>)?; - chain_spec.set_light_sync_state(sync_state.to_serializable()); - let string = chain_spec.as_json(raw).map_err(map_error::)?; + let extension = sc_chain_spec::get_extension_mut::( + chain_spec.extensions_mut(), + ) + .ok_or_else(|| { + Error::::JsonRpc("Could not find `LightSyncState` chain-spec extension!".into()) + })?; - serde_json::from_str(&string).map_err(|err| map_error::(err)) + *extension = + Some(serde_json::to_value(&sync_state).map_err(|err| map_error::(err))?); + + let json_string = chain_spec.as_json(raw).map_err(map_error::)?; + + serde_json::from_str(&json_string).map_err(|err| map_error::(err)) } } diff --git a/substrate/test-utils/test-runner/src/client.rs b/substrate/test-utils/test-runner/src/client.rs index d130993bff..17117e0b5e 100644 --- a/substrate/test-utils/test-runner/src/client.rs +++ b/substrate/test-utils/test-runner/src/client.rs @@ -192,7 +192,7 @@ where rpc_extensions_builder: Box::new(move |_, _| { let mut io = jsonrpc_core::IoHandler::default(); io.extend_with(ManualSealApi::to_delegate(ManualSeal::new(rpc_sink.clone()))); - io + Ok(io) }), remote_blockchain: None, network,