From d7e17fc612b73a32eab98970ebe86159bf59b019 Mon Sep 17 00:00:00 2001
From: Fedor Sakharov <fedor.sakharov@gmail.com>
Date: Thu, 11 Jun 2020 22:26:15 +0300
Subject: [PATCH] New service initial commit (#1234)

* New service initial commit

* More separation of the new and old services

* Fix review comments

* Adds polkadot.json

* Fix browser build

* Remove unused import

* Update node/service/src/lib.rs

Co-authored-by: Fedor Sakharov <fedor.sakharov@gmail.com>

* Remove duplicate json files

Co-authored-by: Robert Habermeier <rphmeier@gmail.com>
---
 polkadot/Cargo.lock                          |  62 ++
 polkadot/Cargo.toml                          |   7 +
 polkadot/cli/Cargo.toml                      |  11 +-
 polkadot/cli/src/command.rs                  |   5 +-
 polkadot/cli/src/lib.rs                      |   8 +
 polkadot/collator/Cargo.toml                 |   8 +-
 polkadot/collator/src/lib.rs                 |  48 +-
 polkadot/node/overseer/src/lib.rs            |   3 +-
 polkadot/node/service/Cargo.toml             |  69 ++
 polkadot/node/service/README.adoc            |   5 +
 polkadot/node/service/src/chain_spec.rs      | 963 +++++++++++++++++++
 polkadot/node/service/src/client.rs          |  53 +
 polkadot/node/service/src/grandpa_support.rs | 363 +++++++
 polkadot/node/service/src/lib.rs             | 729 ++++++++++++++
 14 files changed, 2325 insertions(+), 9 deletions(-)
 create mode 100644 polkadot/node/service/Cargo.toml
 create mode 100644 polkadot/node/service/README.adoc
 create mode 100644 polkadot/node/service/src/chain_spec.rs
 create mode 100644 polkadot/node/service/src/client.rs
 create mode 100644 polkadot/node/service/src/grandpa_support.rs
 create mode 100644 polkadot/node/service/src/lib.rs

diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock
index 396df57e0b6..7377ebcccc6 100644
--- a/polkadot/Cargo.lock
+++ b/polkadot/Cargo.lock
@@ -4071,6 +4071,7 @@ dependencies = [
  "nix 0.17.0",
  "parity-util-mem",
  "polkadot-cli",
+ "polkadot-collator",
  "polkadot-service",
  "tempfile",
 ]
@@ -4109,6 +4110,7 @@ dependencies = [
  "futures 0.3.5",
  "log 0.4.8",
  "polkadot-service",
+ "polkadot-service-new",
  "sc-cli",
  "sc-client-api",
  "sc-client-db",
@@ -4137,6 +4139,7 @@ dependencies = [
  "polkadot-network",
  "polkadot-primitives",
  "polkadot-service",
+ "polkadot-service-new",
  "polkadot-validation",
  "sc-cli",
  "sc-client-api",
@@ -4521,6 +4524,65 @@ dependencies = [
  "westend-runtime",
 ]
 
+[[package]]
+name = "polkadot-service-new"
+version = "0.8.3"
+dependencies = [
+ "env_logger",
+ "frame-benchmarking",
+ "frame-system-rpc-runtime-api",
+ "futures 0.3.5",
+ "hex-literal",
+ "kusama-runtime",
+ "lazy_static",
+ "log 0.4.8",
+ "pallet-babe",
+ "pallet-im-online",
+ "pallet-staking",
+ "pallet-transaction-payment-rpc-runtime-api",
+ "parity-scale-codec",
+ "parking_lot 0.9.0",
+ "polkadot-network",
+ "polkadot-overseer",
+ "polkadot-primitives",
+ "polkadot-rpc",
+ "polkadot-runtime",
+ "polkadot-test-runtime-client",
+ "sc-authority-discovery",
+ "sc-basic-authorship",
+ "sc-block-builder",
+ "sc-chain-spec",
+ "sc-client-api",
+ "sc-client-db",
+ "sc-consensus",
+ "sc-consensus-babe",
+ "sc-executor",
+ "sc-finality-grandpa",
+ "sc-keystore",
+ "sc-network",
+ "sc-service",
+ "sc-telemetry",
+ "sc-transaction-pool",
+ "serde",
+ "slog",
+ "sp-api",
+ "sp-authority-discovery",
+ "sp-block-builder",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-consensus-babe",
+ "sp-core",
+ "sp-finality-grandpa",
+ "sp-inherents",
+ "sp-io",
+ "sp-offchain",
+ "sp-runtime",
+ "sp-session",
+ "sp-transaction-pool",
+ "substrate-prometheus-endpoint",
+ "westend-runtime",
+]
+
 [[package]]
 name = "polkadot-statement-table"
 version = "0.8.7"
diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml
index 7b6d6aed550..2833abcceb7 100644
--- a/polkadot/Cargo.toml
+++ b/polkadot/Cargo.toml
@@ -10,6 +10,8 @@ edition = "2018"
 
 [dependencies]
 cli = { package = "polkadot-cli", path = "cli" }
+# It looks like this is the only way to pass features to it
+collator = { package = "polkadot-collator", path = "collator" }
 futures = "0.3.4"
 service = { package = "polkadot-service", path = "service" }
 parity-util-mem = { version = "*", default-features = false, features = ["jemalloc-global"] }
@@ -42,6 +44,7 @@ members = [
 
 	"node/messages",
 	"node/overseer",
+	"node/service",
 
 	"parachain/test-parachains",
 	"parachain/test-parachains/adder",
@@ -64,3 +67,7 @@ panic = "unwind"
 
 [features]
 runtime-benchmarks=["cli/runtime-benchmarks"]
+service-rewr= [
+	"cli/service-rewr",
+	"collator/service-rewr",
+]
diff --git a/polkadot/cli/Cargo.toml b/polkadot/cli/Cargo.toml
index ec4df900be1..b631dd1e780 100644
--- a/polkadot/cli/Cargo.toml
+++ b/polkadot/cli/Cargo.toml
@@ -23,7 +23,8 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master
 sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sc-client-db = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" }
-service = { package = "polkadot-service", path = "../service", default-features = false }
+service = { package = "polkadot-service", path = "../service", default-features = false, optional = true }
+service-new = { package = "polkadot-service-new", path = "../node/service", default-features = false, optional = true }
 
 tokio = { version = "0.2.13", features = ["rt-threaded"], optional = true }
 frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true }
@@ -38,7 +39,7 @@ browser-utils = { package = "substrate-browser-utils", git = "https://github.com
 substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" }
 
 [features]
-default = [ "wasmtime", "db", "cli" ]
+default = [ "wasmtime", "db", "cli", "service-old" ]
 wasmtime = [ "sc-cli/wasmtime" ]
 db = [ "service/db" ]
 cli = [
@@ -46,11 +47,13 @@ cli = [
 	"sc-cli",
 	"sc-service",
 	"frame-benchmarking-cli",
-	"service/full-node",
 ]
+service-old = [ "service/full-node" ]
 browser = [
 	"wasm-bindgen",
 	"wasm-bindgen-futures",
 	"browser-utils",
+	"service",
 ]
-runtime-benchmarks = ["service/runtime-benchmarks"]
+runtime-benchmarks = [ "service/runtime-benchmarks" ]
+service-rewr = [ "service-new/full-node" ]
diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs
index 9890d39544a..5e9d7838283 100644
--- a/polkadot/cli/src/command.rs
+++ b/polkadot/cli/src/command.rs
@@ -15,7 +15,10 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 use log::info;
+#[cfg(not(feature = "service-rewr"))]
 use service::{IdentifyVariant, self};
+#[cfg(feature = "service-rewr")]
+use service_new::{IdentifyVariant, self as service};
 use sc_executor::NativeExecutionDispatch;
 use sc_cli::{SubstrateCli, Result};
 use crate::cli::{Cli, Subcommand};
@@ -206,7 +209,7 @@ pub fn run() -> Result<()> {
 			if cfg!(feature = "browser") {
 				Err(sc_cli::Error::Input("Cannot run validation worker in browser".into()))
 			} else {
-				#[cfg(not(feature = "browser"))]
+				#[cfg(all(not(feature = "browser"), not(feature = "service-rewr")))]
 				service::run_validation_worker(&cmd.mem_id)?;
 				Ok(())
 			}
diff --git a/polkadot/cli/src/lib.rs b/polkadot/cli/src/lib.rs
index 3e31fa0bb2a..be2f3c6cd64 100644
--- a/polkadot/cli/src/lib.rs
+++ b/polkadot/cli/src/lib.rs
@@ -26,11 +26,19 @@ mod cli;
 #[cfg(feature = "cli")]
 mod command;
 
+#[cfg(not(feature = "service-rewr"))]
 pub use service::{
 	AbstractService, ProvideRuntimeApi, CoreApi, ParachainHost, IdentifyVariant,
 	Block, self, RuntimeApiCollection, TFullClient
 };
 
+#[cfg(feature = "service-rewr")]
+pub use service_new::{
+	self as service,
+	AbstractService, ProvideRuntimeApi, CoreApi, ParachainHost, IdentifyVariant,
+	Block, self, RuntimeApiCollection, TFullClient
+};
+
 #[cfg(feature = "cli")]
 pub use cli::*;
 
diff --git a/polkadot/collator/Cargo.toml b/polkadot/collator/Cargo.toml
index a2e8691e7a6..52d04fba10f 100644
--- a/polkadot/collator/Cargo.toml
+++ b/polkadot/collator/Cargo.toml
@@ -21,7 +21,8 @@ polkadot-primitives = { path = "../primitives" }
 polkadot-cli = { path = "../cli" }
 polkadot-network = { path = "../network" }
 polkadot-validation = { path = "../validation" }
-polkadot-service = { path = "../service" }
+polkadot-service = { path = "../service", optional = true}
+polkadot-service-new = { path = "../node/service", optional = true }
 log = "0.4.8"
 tokio = "0.2.13"
 futures-timer = "2.0"
@@ -29,3 +30,8 @@ codec = { package = "parity-scale-codec", version = "1.3.0" }
 
 [dev-dependencies]
 keyring = { package = "sp-keyring", git = "https://github.com/paritytech/substrate", branch = "master" }
+
+[features]
+default = ["service-old"]
+service-old = [ "polkadot-service" ]
+service-rewr = [ "polkadot-service-new" ]
diff --git a/polkadot/collator/src/lib.rs b/polkadot/collator/src/lib.rs
index 08706a7cb09..932648f3e15 100644
--- a/polkadot/collator/src/lib.rs
+++ b/polkadot/collator/src/lib.rs
@@ -74,7 +74,13 @@ pub use sc_network::PeerId;
 pub use service::RuntimeApiCollection;
 pub use sc_cli::SubstrateCli;
 use sp_api::{ConstructRuntimeApi, ApiExt, HashFor};
-use polkadot_service::PolkadotClient;
+#[cfg(not(feature = "service-rewr"))]
+use polkadot_service::{FullNodeHandles, PolkadotClient};
+#[cfg(feature = "service-rewr")]
+use polkadot_service_new::{
+	self as polkadot_service,
+	Error as ServiceError, FullNodeHandles, PolkadotClient,
+};
 
 const COLLATION_TIMEOUT: Duration = Duration::from_secs(30);
 
@@ -201,9 +207,46 @@ pub async fn collate<P>(
 	Ok(collation)
 }
 
+#[cfg(feature = "service-rewr")]
+fn build_collator_service<SP, P, C, R, Extrinsic>(
+	_spawner: SP,
+	_handles: FullNodeHandles,
+	_client: Arc<C>,
+	_para_id: ParaId,
+	_key: Arc<CollatorPair>,
+	_build_parachain_context: P,
+) -> Result<future::Ready<()>, polkadot_service::Error>
+	where
+		C: PolkadotClient<
+			service::Block,
+			service::TFullBackend<service::Block>,
+			R
+		> + 'static,
+		R: ConstructRuntimeApi<service::Block, C> + Sync + Send,
+		<R as ConstructRuntimeApi<service::Block, C>>::RuntimeApi:
+			sp_api::ApiExt<
+				service::Block,
+				StateBackend = <service::TFullBackend<service::Block> as service::Backend<service::Block>>::State,
+			>
+			+ RuntimeApiCollection<
+				Extrinsic,
+				StateBackend = <service::TFullBackend<service::Block> as service::Backend<service::Block>>::State,
+			>
+			+ Sync + Send,
+		P: BuildParachainContext,
+		P::ParachainContext: Send + 'static,
+		<P::ParachainContext as ParachainContext>::ProduceCandidate: Send,
+		Extrinsic: service::Codec + Send + Sync + 'static,
+		SP: Spawn + Clone + Send + Sync + 'static,
+{
+	Err("Collator is not functional with the new service yet".into())
+}
+
+
+#[cfg(not(feature = "service-rewr"))]
 fn build_collator_service<SP, P, C, R, Extrinsic>(
 	spawner: SP,
-	handles: polkadot_service::FullNodeHandles,
+	handles: FullNodeHandles,
 	client: Arc<C>,
 	para_id: ParaId,
 	key: Arc<CollatorPair>,
@@ -408,6 +451,7 @@ where
 	Ok(())
 }
 
+#[cfg(not(feature = "service-rewr"))]
 fn compute_targets(para_id: ParaId, session_keys: &[ValidatorId], roster: DutyRoster) -> HashSet<ValidatorId> {
 	use polkadot_primitives::parachain::Chain;
 
diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs
index abe648bfa19..56bf953f61d 100644
--- a/polkadot/node/overseer/src/lib.rs
+++ b/polkadot/node/overseer/src/lib.rs
@@ -56,6 +56,7 @@
 
 use std::fmt::Debug;
 use std::pin::Pin;
+use std::sync::Arc;
 use std::task::Poll;
 use std::time::Duration;
 use std::collections::HashSet;
@@ -232,7 +233,7 @@ impl OverseerHandler {
 /// [`Overseer`]: struct.Overseer.html
 /// [`OverseerHandler`]: struct.OverseerHandler.html
 pub async fn forward_events<P: BlockchainEvents<Block>>(
-	client: P,
+	client: Arc<P>,
 	mut handler: OverseerHandler,
 ) -> SubsystemResult<()> {
 	let mut finality = client.finality_notification_stream();
diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml
new file mode 100644
index 00000000000..74069f0233a
--- /dev/null
+++ b/polkadot/node/service/Cargo.toml
@@ -0,0 +1,69 @@
+[package]
+name = "polkadot-service-new"
+version = "0.8.3"
+authors = ["Parity Technologies <admin@parity.io>"]
+edition = "2018"
+
+[dependencies]
+parking_lot = "0.9.0"
+serde = { version = "1.0.102", features = ["derive"] }
+lazy_static = "1.4.0"
+log = "0.4.8"
+futures = "0.3.4"
+slog = "2.5.2"
+hex-literal = "0.2.1"
+polkadot-primitives = { path = "../../primitives" }
+polkadot-runtime = { path = "../../runtime/polkadot" }
+polkadot-overseer = { path = "../overseer" }
+kusama-runtime = { path = "../../runtime/kusama" }
+westend-runtime = { path = "../../runtime/westend" }
+polkadot-network = { path = "../../network", optional = true }
+polkadot-rpc = { path = "../../rpc" }
+sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-client-db = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" }
+consensus_common = { package = "sp-consensus", git = "https://github.com/paritytech/substrate", branch = "master" }
+grandpa = { package = "sc-finality-grandpa", git = "https://github.com/paritytech/substrate", branch = "master" }
+grandpa_primitives = { package = "sp-finality-grandpa", git = "https://github.com/paritytech/substrate", branch = "master" }
+inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master" }
+service = { package = "sc-service", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
+telemetry = { package = "sc-telemetry", git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
+pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master" }
+pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master" }
+im-online = { package = "pallet-im-online", git = "https://github.com/paritytech/substrate", branch = "master" }
+authority-discovery = { package = "sc-authority-discovery", git = "https://github.com/paritytech/substrate", branch = "master" }
+authority-discovery-primitives = { package = "sp-authority-discovery", git = "https://github.com/paritytech/substrate", branch = "master" }
+babe = { package = "sc-consensus-babe", git = "https://github.com/paritytech/substrate", branch = "master" }
+babe-primitives = { package = "sp-consensus-babe", git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
+pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
+system_rpc_runtime_api = { package = "frame-system-rpc-runtime-api", git = "https://github.com/paritytech/substrate", branch = "master" }
+codec = { package = "parity-scale-codec", version = "1.3.0" }
+sp-session = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-offchain = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master" }
+prometheus-endpoint = { package = "substrate-prometheus-endpoint", git = "https://github.com/paritytech/substrate", branch = "master" }
+frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" }
+
+[dev-dependencies]
+polkadot-test-runtime-client = { path = "../../runtime/test-runtime/client" }
+sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
+env_logger = "0.7.0"
+
+[features]
+default = ["db", "full-node"]
+db = ["service/db"]
+runtime-benchmarks = ["polkadot-runtime/runtime-benchmarks", "kusama-runtime/runtime-benchmarks", "westend-runtime/runtime-benchmarks"]
+full-node = []
diff --git a/polkadot/node/service/README.adoc b/polkadot/node/service/README.adoc
new file mode 100644
index 00000000000..2196d546780
--- /dev/null
+++ b/polkadot/node/service/README.adoc
@@ -0,0 +1,5 @@
+
+= Polkadot Service
+
+placeholder
+//TODO Write content :) (https://github.com/paritytech/polkadot/issues/159)
diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs
new file mode 100644
index 00000000000..0659d080830
--- /dev/null
+++ b/polkadot/node/service/src/chain_spec.rs
@@ -0,0 +1,963 @@
+// Copyright 2017-2020 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot 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.
+
+// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Polkadot chain configurations.
+
+use sp_core::{Pair, Public, crypto::UncheckedInto, sr25519};
+use polkadot_primitives::{AccountId, AccountPublic, parachain::ValidatorId};
+use polkadot_runtime as polkadot;
+use kusama_runtime as kusama;
+use westend_runtime as westend;
+use polkadot::constants::currency::DOTS;
+use kusama::constants::currency::DOTS as KSM;
+use westend::constants::currency::DOTS as WND;
+use sc_chain_spec::{ChainSpecExtension, ChainType};
+use sp_runtime::{traits::IdentifyAccount, Perbill};
+use serde::{Serialize, Deserialize};
+use telemetry::TelemetryEndpoints;
+use hex_literal::hex;
+use babe_primitives::AuthorityId as BabeId;
+use grandpa::AuthorityId as GrandpaId;
+use im_online::sr25519::{AuthorityId as ImOnlineId};
+use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId;
+use pallet_staking::Forcing;
+
+const POLKADOT_STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
+const KUSAMA_STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
+const WESTEND_STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
+const DEFAULT_PROTOCOL_ID: &str = "dot";
+
+/// Node `ChainSpec` extensions.
+///
+/// Additional parameters for some Substrate core modules,
+/// customizable from the chain spec.
+#[derive(Default, Clone, Serialize, Deserialize, ChainSpecExtension)]
+#[serde(rename_all = "camelCase")]
+pub struct Extensions {
+	/// Block numbers with known hashes.
+	pub fork_blocks: sc_client_api::ForkBlocks<polkadot_primitives::Block>,
+	/// Known bad block hashes.
+	pub bad_blocks: sc_client_api::BadBlocks<polkadot_primitives::Block>,
+}
+
+/// The `ChainSpec parametrised for polkadot runtime`.
+pub type PolkadotChainSpec = service::GenericChainSpec<
+	polkadot::GenesisConfig,
+	Extensions,
+>;
+
+/// The `ChainSpec parametrised for kusama runtime`.
+pub type KusamaChainSpec = service::GenericChainSpec<
+	kusama::GenesisConfig,
+	Extensions,
+>;
+
+/// The `ChainSpec parametrised for westend runtime`.
+pub type WestendChainSpec = service::GenericChainSpec<
+	westend::GenesisConfig,
+	Extensions,
+>;
+
+pub fn polkadot_config() -> Result<PolkadotChainSpec, String> {
+	PolkadotChainSpec::from_json_bytes(&include_bytes!("../../../service/res/polkadot.json")[..])
+}
+
+pub fn kusama_config() -> Result<KusamaChainSpec, String> {
+	KusamaChainSpec::from_json_bytes(&include_bytes!("../../../service/res/kusama.json")[..])
+}
+
+pub fn westend_config() -> Result<PolkadotChainSpec, String> {
+	PolkadotChainSpec::from_json_bytes(&include_bytes!("../../../service/res/westend.json")[..])
+}
+
+fn polkadot_session_keys(
+	babe: BabeId,
+	grandpa: GrandpaId,
+	im_online: ImOnlineId,
+	parachain_validator: ValidatorId,
+	authority_discovery: AuthorityDiscoveryId
+) -> polkadot::SessionKeys {
+	polkadot::SessionKeys { babe, grandpa, im_online, parachain_validator, authority_discovery }
+}
+
+fn kusama_session_keys(
+	babe: BabeId,
+	grandpa: GrandpaId,
+	im_online: ImOnlineId,
+	parachain_validator: ValidatorId,
+	authority_discovery: AuthorityDiscoveryId
+) -> kusama::SessionKeys {
+	kusama::SessionKeys { babe, grandpa, im_online, parachain_validator, authority_discovery }
+}
+
+fn westend_session_keys(
+	babe: BabeId,
+	grandpa: GrandpaId,
+	im_online: ImOnlineId,
+	parachain_validator: ValidatorId,
+	authority_discovery: AuthorityDiscoveryId
+) -> westend::SessionKeys {
+	westend::SessionKeys { babe, grandpa, im_online, parachain_validator, authority_discovery }
+}
+
+fn polkadot_staging_testnet_config_genesis() -> polkadot::GenesisConfig {
+	// subkey inspect "$SECRET"
+	let endowed_accounts = vec![];
+
+	let initial_authorities: Vec<(
+		AccountId,
+		AccountId,
+		BabeId,
+		GrandpaId,
+		ImOnlineId,
+		ValidatorId,
+		AuthorityDiscoveryId
+	)> = vec![];
+
+	const ENDOWMENT: u128 = 1_000_000 * DOTS;
+	const STASH: u128 = 100 * DOTS;
+
+	polkadot::GenesisConfig {
+		system: Some(polkadot::SystemConfig {
+			code: polkadot::WASM_BINARY.to_vec(),
+			changes_trie_config: Default::default(),
+		}),
+		balances: Some(polkadot::BalancesConfig {
+			balances: endowed_accounts.iter()
+				.map(|k: &AccountId| (k.clone(), ENDOWMENT))
+				.chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
+				.collect(),
+		}),
+		indices: Some(polkadot::IndicesConfig {
+			indices: vec![],
+		}),
+		session: Some(polkadot::SessionConfig {
+			keys: initial_authorities.iter().map(|x| (
+				x.0.clone(),
+				x.0.clone(),
+				polkadot_session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone(), x.6.clone()),
+			)).collect::<Vec<_>>(),
+		}),
+		staking: Some(polkadot::StakingConfig {
+			validator_count: 50,
+			minimum_validator_count: 4,
+			stakers: initial_authorities
+				.iter()
+				.map(|x| (x.0.clone(), x.1.clone(), STASH, polkadot::StakerStatus::Validator))
+				.collect(),
+			invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+			force_era: Forcing::ForceNone,
+			slash_reward_fraction: Perbill::from_percent(10),
+			.. Default::default()
+		}),
+		elections_phragmen: Some(Default::default()),
+		democracy: Some(Default::default()),
+		collective_Instance1: Some(polkadot::CouncilConfig {
+			members: vec![],
+			phantom: Default::default(),
+		}),
+		collective_Instance2: Some(polkadot::TechnicalCommitteeConfig {
+			members: vec![],
+			phantom: Default::default(),
+		}),
+		membership_Instance1: Some(Default::default()),
+		babe: Some(Default::default()),
+		grandpa: Some(Default::default()),
+		im_online: Some(Default::default()),
+		authority_discovery: Some(polkadot::AuthorityDiscoveryConfig {
+			keys: vec![],
+		}),
+		parachains: Some(polkadot::ParachainsConfig {
+			authorities: vec![],
+		}),
+		registrar: Some(polkadot::RegistrarConfig {
+			parachains: vec![],
+			_phdata: Default::default(),
+		}),
+		claims: Some(polkadot::ClaimsConfig {
+			claims: vec![],
+			vesting: vec![],
+		}),
+		vesting: Some(polkadot::VestingConfig {
+			vesting: vec![],
+		}),
+		sudo: Some(polkadot::SudoConfig {
+			key: endowed_accounts[0].clone(),
+		}),
+	}
+}
+
+fn westend_staging_testnet_config_genesis() -> westend::GenesisConfig {
+// subkey inspect "$SECRET"
+	let endowed_accounts = vec![
+		// 5ENpP27BrVdJTdUfY6djmcw3d3xEJ6NzSUU52CCPmGpMrdEY
+		hex!["6648d7f3382690650c681aba1b993cd11e54deb4df21a3a18c3e2177de9f7342"].into(),
+	];
+
+	// for i in 1 2 3 4; do for j in stash controller; do subkey inspect "$SECRET//$i//$j"; done; done
+	// for i in 1 2 3 4; do for j in babe; do subkey --sr25519 inspect "$SECRET//$i//$j"; done; done
+	// for i in 1 2 3 4; do for j in grandpa; do subkey --ed25519 inspect "$SECRET//$i//$j"; done; done
+	// for i in 1 2 3 4; do for j in im_online; do subkey --sr25519 inspect "$SECRET//$i//$j"; done; done
+	// for i in 1 2 3 4; do for j in parachains; do subkey --sr25519 inspect "$SECRET//$i//$j"; done; done
+	let initial_authorities: Vec<(
+		AccountId,
+		AccountId,
+		BabeId,
+		GrandpaId,
+		ImOnlineId,
+		ValidatorId,
+		AuthorityDiscoveryId
+	)> = vec![(
+		// 5FZoQhgUCmqBxnkHX7jCqThScS2xQWiwiF61msg63CFL3Y8f
+		hex!["9ae581fef1fc06828723715731adcf810e42ce4dadad629b1b7fa5c3c144a81d"].into(),
+		// 5ExdKyXFhtrjiFhexnyQPDyGSP8xU9qHc4KDwVrtWxaP2RP6
+		hex!["8011fb3641f0641f5570ba8787a64a0ff7d9c9999481f333d7207c4abd7e981c"].into(),
+		// 5Ef8qY8LRV6RFd4bThrwxBhhWfLjzqmd4rK8nX3Xs7zJqqp7
+		hex!["72bae70a1398c0ba52f815cc5dfbc9ec5c013771e541ae28e05d1129243e3001"].unchecked_into(),
+		// 5FSscBiPfaPaEhFbAt2qRhcYjryKBKf714X76F5nFfwtdXLa
+		hex!["959cebf18fecb305b96fd998c95f850145f52cbbb64b3ef937c0575cc7ebd652"].unchecked_into(),
+		// 5Ef8qY8LRV6RFd4bThrwxBhhWfLjzqmd4rK8nX3Xs7zJqqp7
+		hex!["72bae70a1398c0ba52f815cc5dfbc9ec5c013771e541ae28e05d1129243e3001"].unchecked_into(),
+		// 5Ef8qY8LRV6RFd4bThrwxBhhWfLjzqmd4rK8nX3Xs7zJqqp7
+		hex!["72bae70a1398c0ba52f815cc5dfbc9ec5c013771e541ae28e05d1129243e3001"].unchecked_into(),
+		// 5Ef8qY8LRV6RFd4bThrwxBhhWfLjzqmd4rK8nX3Xs7zJqqp7
+		hex!["72bae70a1398c0ba52f815cc5dfbc9ec5c013771e541ae28e05d1129243e3001"].unchecked_into(),
+	),(
+		// 5G1ojzh47Yt8KoYhuAjXpHcazvsoCXe3G8LZchKDvumozJJJ
+		hex!["aebb0211dbb07b4d335a657257b8ac5e53794c901e4f616d4a254f2490c43934"].into(),
+		// 5GeoZ1Mzix6Xnj32X8Xpj7q89X1SQHU5XTK1cnUVNXKTvXdK
+		hex!["caf27345aebc2fefeca85c9a67f4859eab3178d28ef92244714402290f3f415a"].into(),
+		// 5Et8y49AyE7ncVKiSRgzN6zbqbYtMK6y7kKuUaS8YqvfLBD9
+		hex!["7ca58770eb41c1a68ef77e92255e4635fc11f665cb89aee469e920511c48343a"].unchecked_into(),
+		// 5Hpn3HVViECsuxMDFtinWjRj2dNfpRp1kB24nZHvQCJsSUek
+		hex!["feca0be2c87141f6074b221c919c0161a1c468d9173c5c1be59b68fab9a0ff93"].unchecked_into(),
+		// 5Et8y49AyE7ncVKiSRgzN6zbqbYtMK6y7kKuUaS8YqvfLBD9
+		hex!["7ca58770eb41c1a68ef77e92255e4635fc11f665cb89aee469e920511c48343a"].unchecked_into(),
+		// 5Et8y49AyE7ncVKiSRgzN6zbqbYtMK6y7kKuUaS8YqvfLBD9
+		hex!["7ca58770eb41c1a68ef77e92255e4635fc11f665cb89aee469e920511c48343a"].unchecked_into(),
+		// 5Et8y49AyE7ncVKiSRgzN6zbqbYtMK6y7kKuUaS8YqvfLBD9
+		hex!["7ca58770eb41c1a68ef77e92255e4635fc11f665cb89aee469e920511c48343a"].unchecked_into(),
+	),(
+		// 5HYYWyhyUQ7Ae11f8fCid58bhJ7ikLHM9bU8A6Ynwoc3dStR
+		hex!["f268995cc38974ce0686df1364875f26f2c32b246ddc18835512c3f9969f5836"].into(),
+		// 5DnUXT3xiQn6ZRttFT6eSCJbT9P2tiLdexr5WsvnbLG8igqW
+		hex!["4c17a9bfdd19411f452fa32420fa7acab622e87e57351f4ba3248ae40ce75123"].into(),
+		// 5EhnN1SumSv5KxwLAdwE8ugJaw1S8xARZb8V2BMYCKaD7ure
+		hex!["74bfb70627416e6e6c4785e928ced384c6c06e5c8dd173a094bc3118da7b673e"].unchecked_into(),
+		// 5Hmvd2qjb1zatrJTkPwgFicxPfZuwaTwa2L7adSRmz6mVxfb
+		hex!["fc9d33059580a69454179ffa41cbae6de2bc8d2bd2c3f1d018fe5484a5a91956"].unchecked_into(),
+		// 5EhnN1SumSv5KxwLAdwE8ugJaw1S8xARZb8V2BMYCKaD7ure
+		hex!["74bfb70627416e6e6c4785e928ced384c6c06e5c8dd173a094bc3118da7b673e"].unchecked_into(),
+		// 5EhnN1SumSv5KxwLAdwE8ugJaw1S8xARZb8V2BMYCKaD7ure
+		hex!["74bfb70627416e6e6c4785e928ced384c6c06e5c8dd173a094bc3118da7b673e"].unchecked_into(),
+		// 5EhnN1SumSv5KxwLAdwE8ugJaw1S8xARZb8V2BMYCKaD7ure
+		hex!["74bfb70627416e6e6c4785e928ced384c6c06e5c8dd173a094bc3118da7b673e"].unchecked_into(),
+	),(
+		// 5CFPcUJgYgWryPaV1aYjSbTpbTLu42V32Ytw1L9rfoMAsfGh
+		hex!["08264834504a64ace1373f0c8ed5d57381ddf54a2f67a318fa42b1352681606d"].into(),
+		// 5F6z64cYZFRAmyMUhp7rnge6jaZmbY6o7XfA9czJyuAUiaFD
+		hex!["8671d451c3d4f6de8c16ea0bc61cf714914d6b2ffa2899872620525419327478"].into(),
+		// 5Ft7o2uqDq5pXCK4g5wR94BctmtLEzCBy5MvPqRa8753ZemD
+		hex!["a8ddd0891e14725841cd1b5581d23806a97f41c28a25436db6473c86e15dcd4f"].unchecked_into(),
+		// 5FgBijJLL6p7nDZgQed56L3BM7ovgwc4t4FYsv9apYtRGAGv
+		hex!["9fc415cce1d0b2eed702c9e05f476217d23b46a8723fd56f08cddad650be7c2d"].unchecked_into(),
+		// 5Ft7o2uqDq5pXCK4g5wR94BctmtLEzCBy5MvPqRa8753ZemD
+		hex!["a8ddd0891e14725841cd1b5581d23806a97f41c28a25436db6473c86e15dcd4f"].unchecked_into(),
+		// 5Ft7o2uqDq5pXCK4g5wR94BctmtLEzCBy5MvPqRa8753ZemD
+		hex!["a8ddd0891e14725841cd1b5581d23806a97f41c28a25436db6473c86e15dcd4f"].unchecked_into(),
+		// 5Ft7o2uqDq5pXCK4g5wR94BctmtLEzCBy5MvPqRa8753ZemD
+		hex!["a8ddd0891e14725841cd1b5581d23806a97f41c28a25436db6473c86e15dcd4f"].unchecked_into(),
+	)];
+
+	const ENDOWMENT: u128 = 1_000_000 * WND;
+	const STASH: u128 = 100 * WND;
+
+	westend::GenesisConfig {
+		system: Some(westend::SystemConfig {
+			code: westend::WASM_BINARY.to_vec(),
+			changes_trie_config: Default::default(),
+		}),
+		balances: Some(westend::BalancesConfig {
+			balances: endowed_accounts.iter()
+				.map(|k: &AccountId| (k.clone(), ENDOWMENT))
+				.chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
+				.collect(),
+		}),
+		indices: Some(westend::IndicesConfig {
+			indices: vec![],
+		}),
+		session: Some(westend::SessionConfig {
+			keys: initial_authorities.iter().map(|x| (
+				x.0.clone(),
+				x.0.clone(),
+				westend_session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone(), x.6.clone()),
+			)).collect::<Vec<_>>(),
+		}),
+		staking: Some(westend::StakingConfig {
+			validator_count: 50,
+			minimum_validator_count: 4,
+			stakers: initial_authorities
+				.iter()
+				.map(|x| (x.0.clone(), x.1.clone(), STASH, westend::StakerStatus::Validator))
+				.collect(),
+			invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+			force_era: Forcing::ForceNone,
+			slash_reward_fraction: Perbill::from_percent(10),
+			.. Default::default()
+		}),
+		babe: Some(Default::default()),
+		grandpa: Some(Default::default()),
+		im_online: Some(Default::default()),
+		authority_discovery: Some(westend::AuthorityDiscoveryConfig {
+			keys: vec![],
+		}),
+		parachains: Some(westend::ParachainsConfig {
+			authorities: vec![],
+		}),
+		registrar: Some(westend::RegistrarConfig {
+			parachains: vec![],
+			_phdata: Default::default(),
+		}),
+		vesting: Some(westend::VestingConfig {
+			vesting: vec![],
+		}),
+		sudo: Some(westend::SudoConfig {
+			key: endowed_accounts[0].clone(),
+		}),
+	}
+}
+
+fn kusama_staging_testnet_config_genesis() -> kusama::GenesisConfig {
+	// subkey inspect "$SECRET"
+	let endowed_accounts = vec![
+		// 5CVFESwfkk7NmhQ6FwHCM9roBvr9BGa4vJHFYU8DnGQxrXvz
+		hex!["12b782529c22032ed4694e0f6e7d486be7daa6d12088f6bc74d593b3900b8438"].into(),
+	];
+
+	// for i in 1 2 3 4; do for j in stash controller; do subkey inspect "$SECRET//$i//$j"; done; done
+	// for i in 1 2 3 4; do for j in babe; do subkey --sr25519 inspect "$SECRET//$i//$j"; done; done
+	// for i in 1 2 3 4; do for j in grandpa; do subkey --ed25519 inspect "$SECRET//$i//$j"; done; done
+	// for i in 1 2 3 4; do for j in im_online; do subkey --sr25519 inspect "$SECRET//$i//$j"; done; done
+	// for i in 1 2 3 4; do for j in parachains; do subkey --sr25519 inspect "$SECRET//$i//$j"; done; done
+	let initial_authorities: Vec<(
+		AccountId,
+		AccountId,
+		BabeId,
+		GrandpaId,
+		ImOnlineId,
+		ValidatorId,
+		AuthorityDiscoveryId
+	)> = vec![(
+	// 5DD7Q4VEfPTLEdn11CnThoHT5f9xKCrnofWJL5SsvpTghaAT
+	hex!["32a5718e87d16071756d4b1370c411bbbb947eb62f0e6e0b937d5cbfc0ea633b"].into(),
+	// 5GNzaEqhrZAtUQhbMe2gn9jBuNWfamWFZHULryFwBUXyd1cG
+	hex!["bee39fe862c85c91aaf343e130d30b643c6ea0b4406a980206f1df8331f7093b"].into(),
+	// 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1
+	hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"].unchecked_into(),
+	// 5EjvdwATjyFFikdZibVvx1q5uBHhphS2Mnsq5c7yfaYK25vm
+	hex!["76620f7c98bce8619979c2b58cf2b0aff71824126d2b039358729dad993223db"].unchecked_into(),
+	// 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1
+	hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"].unchecked_into(),
+	// 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1
+	hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"].unchecked_into(),
+	// 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1
+	hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"].unchecked_into(),
+	),(
+	// 5G9VGb8ESBeS8Ca4or43RfhShzk9y7T5iTmxHk5RJsjZwsRx
+	hex!["b496c98a405ceab59b9e970e59ef61acd7765a19b704e02ab06c1cdfe171e40f"].into(),
+	// 5F7V9Y5FcxKXe1aroqvPeRiUmmeQwTFcL3u9rrPXcMuMiCNx
+	hex!["86d3a7571dd60139d297e55d8238d0c977b2e208c5af088f7f0136b565b0c103"].into(),
+	// 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY
+	hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"].unchecked_into(),
+	// 5HBDAaybNqjmY7ww8ZcZZY1L5LHxvpnyfqJwoB7HhR6raTmG
+	hex!["e2234d661bee4a04c38392c75d1566200aa9e6ae44dd98ee8765e4cc9af63cb7"].unchecked_into(),
+	// 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY
+	hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"].unchecked_into(),
+	// 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY
+	hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"].unchecked_into(),
+	// 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY
+	hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"].unchecked_into(),
+	),(
+	// 5FzwpgGvk2kk9agow6KsywLYcPzjYc8suKej2bne5G5b9YU3
+	hex!["ae12f70078a22882bf5135d134468f77301927aa67c376e8c55b7ff127ace115"].into(),
+	// 5EqoZhVC2BcsM4WjvZNidu2muKAbu5THQTBKe3EjvxXkdP7A
+	hex!["7addb914ec8486bbc60643d2647685dcc06373401fa80e09813b630c5831d54b"].into(),
+	// 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5
+	hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"].unchecked_into(),
+	// 5E8ULLQrDAtWhfnVfZmX41Yux86zNAwVJYguWJZVWrJvdhBe
+	hex!["5b57ed1443c8967f461db1f6eb2ada24794d163a668f1cf9d9ce3235dfad8799"].unchecked_into(),
+	// 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5
+	hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"].unchecked_into(),
+	// 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5
+	hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"].unchecked_into(),
+	// 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5
+	hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"].unchecked_into(),
+	),(
+	// 5CFj6Kg9rmVn1vrqpyjau2ztyBzKeVdRKwNPiA3tqhB5HPqq
+	hex!["0867dbb49721126df589db100dda728dc3b475cbf414dad8f72a1d5e84897252"].into(),
+	// 5CwQXP6nvWzigFqNhh2jvCaW9zWVzkdveCJY3tz2MhXMjTon
+	hex!["26ab2b4b2eba2263b1e55ceb48f687bb0018130a88df0712fbdaf6a347d50e2a"].into(),
+	// 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd
+	hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"].unchecked_into(),
+	// 5HGLmrZsiTFTPp3QoS1W8w9NxByt8PVq79reqvdxNcQkByqK
+	hex!["e60d23f49e93c1c1f2d7c115957df5bbd7faf5ebf138d1e9d02e8b39a1f63df0"].unchecked_into(),
+	// 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd
+	hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"].unchecked_into(),
+	// 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd
+	hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"].unchecked_into(),
+	// 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd
+	hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"].unchecked_into(),
+	)];
+
+	const ENDOWMENT: u128 = 1_000_000 * KSM;
+	const STASH: u128 = 100 * KSM;
+
+	kusama::GenesisConfig {
+		system: Some(kusama::SystemConfig {
+			code: kusama::WASM_BINARY.to_vec(),
+			changes_trie_config: Default::default(),
+		}),
+		balances: Some(kusama::BalancesConfig {
+			balances: endowed_accounts.iter()
+				.map(|k: &AccountId| (k.clone(), ENDOWMENT))
+				.chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
+				.collect(),
+		}),
+		indices: Some(kusama::IndicesConfig {
+			indices: vec![],
+		}),
+		session: Some(kusama::SessionConfig {
+			keys: initial_authorities.iter().map(|x| (
+						  x.0.clone(),
+						  x.0.clone(),
+						  kusama_session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone(), x.6.clone()),
+				  )).collect::<Vec<_>>(),
+		}),
+		staking: Some(kusama::StakingConfig {
+			validator_count: 50,
+			minimum_validator_count: 4,
+			stakers: initial_authorities
+				.iter()
+				.map(|x| (x.0.clone(), x.1.clone(), STASH, kusama::StakerStatus::Validator))
+				.collect(),
+				invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+				force_era: Forcing::ForceNone,
+				slash_reward_fraction: Perbill::from_percent(10),
+				.. Default::default()
+		}),
+		elections_phragmen: Some(Default::default()),
+		democracy: Some(Default::default()),
+		collective_Instance1: Some(kusama::CouncilConfig {
+			members: vec![],
+			phantom: Default::default(),
+		}),
+		collective_Instance2: Some(kusama::TechnicalCommitteeConfig {
+			members: vec![],
+			phantom: Default::default(),
+		}),
+		membership_Instance1: Some(Default::default()),
+		babe: Some(Default::default()),
+		grandpa: Some(Default::default()),
+		im_online: Some(Default::default()),
+		authority_discovery: Some(kusama::AuthorityDiscoveryConfig {
+			keys: vec![],
+		}),
+		parachains: Some(kusama::ParachainsConfig {
+			authorities: vec![],
+		}),
+		registrar: Some(kusama::RegistrarConfig {
+			parachains: vec![],
+			_phdata: Default::default(),
+		}),
+		claims: Some(kusama::ClaimsConfig {
+			claims: vec![],
+			vesting: vec![],
+		}),
+		vesting: Some(kusama::VestingConfig {
+			vesting: vec![],
+		}),
+	}
+}
+
+/// Polkadot staging testnet config.
+pub fn polkadot_staging_testnet_config() -> PolkadotChainSpec {
+	let boot_nodes = vec![];
+	PolkadotChainSpec::from_genesis(
+		"Polkadot Staging Testnet",
+		"polkadot_staging_testnet",
+		ChainType::Live,
+		polkadot_staging_testnet_config_genesis,
+		boot_nodes,
+		Some(TelemetryEndpoints::new(vec![(POLKADOT_STAGING_TELEMETRY_URL.to_string(), 0)])
+			.expect("Polkadot Staging telemetry url is valid; qed")),
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
+
+/// Staging testnet config.
+pub fn kusama_staging_testnet_config() -> KusamaChainSpec {
+	let boot_nodes = vec![];
+	KusamaChainSpec::from_genesis(
+		"Kusama Staging Testnet",
+		"kusama_staging_testnet",
+		ChainType::Live,
+		kusama_staging_testnet_config_genesis,
+		boot_nodes,
+		Some(TelemetryEndpoints::new(vec![(KUSAMA_STAGING_TELEMETRY_URL.to_string(), 0)])
+			.expect("Kusama Staging telemetry url is valid; qed")),
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
+
+/// Westend staging testnet config.
+pub fn westend_staging_testnet_config() -> WestendChainSpec {
+	let boot_nodes = vec![];
+	WestendChainSpec::from_genesis(
+		"Westend Staging Testnet",
+		"westend_staging_testnet",
+		ChainType::Live,
+		westend_staging_testnet_config_genesis,
+		boot_nodes,
+		Some(TelemetryEndpoints::new(vec![(WESTEND_STAGING_TELEMETRY_URL.to_string(), 0)])
+			.expect("Westend Staging telemetry url is valid; qed")),
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
+
+/// Helper function to generate a crypto pair from seed
+pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
+	TPublic::Pair::from_string(&format!("//{}", seed), None)
+		.expect("static values are valid; qed")
+		.public()
+}
+
+
+/// Helper function to generate an account ID from seed
+pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where
+	AccountPublic: From<<TPublic::Pair as Pair>::Public>
+{
+	AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
+}
+
+/// Helper function to generate stash, controller and session key from seed
+pub fn get_authority_keys_from_seed(seed: &str) -> (
+	AccountId,
+	AccountId,
+	BabeId,
+	GrandpaId,
+	ImOnlineId,
+	ValidatorId,
+	AuthorityDiscoveryId
+) {
+	(
+		get_account_id_from_seed::<sr25519::Public>(&format!("{}//stash", seed)),
+		get_account_id_from_seed::<sr25519::Public>(seed),
+		get_from_seed::<BabeId>(seed),
+		get_from_seed::<GrandpaId>(seed),
+		get_from_seed::<ImOnlineId>(seed),
+		get_from_seed::<ValidatorId>(seed),
+		get_from_seed::<AuthorityDiscoveryId>(seed),
+	)
+}
+
+fn testnet_accounts() -> Vec<AccountId> {
+	vec![
+		get_account_id_from_seed::<sr25519::Public>("Alice"),
+		get_account_id_from_seed::<sr25519::Public>("Bob"),
+		get_account_id_from_seed::<sr25519::Public>("Charlie"),
+		get_account_id_from_seed::<sr25519::Public>("Dave"),
+		get_account_id_from_seed::<sr25519::Public>("Eve"),
+		get_account_id_from_seed::<sr25519::Public>("Ferdie"),
+		get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+		get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+		get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
+		get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
+		get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
+		get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
+	]
+}
+
+/// Helper function to create polkadot GenesisConfig for testing
+pub fn polkadot_testnet_genesis(
+	initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId, ImOnlineId, ValidatorId, AuthorityDiscoveryId)>,
+	root_key: AccountId,
+	endowed_accounts: Option<Vec<AccountId>>,
+) -> polkadot::GenesisConfig {
+	let endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(testnet_accounts);
+
+	const ENDOWMENT: u128 = 1_000_000 * DOTS;
+	const STASH: u128 = 100 * DOTS;
+
+	polkadot::GenesisConfig {
+		system: Some(polkadot::SystemConfig {
+			code: polkadot::WASM_BINARY.to_vec(),
+			changes_trie_config: Default::default(),
+		}),
+		indices: Some(polkadot::IndicesConfig {
+			indices: vec![],
+		}),
+		balances: Some(polkadot::BalancesConfig {
+			balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(),
+		}),
+		session: Some(polkadot::SessionConfig {
+			keys: initial_authorities.iter().map(|x| (
+						  x.0.clone(),
+						  x.0.clone(),
+						  polkadot_session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone(), x.6.clone()),
+				  )).collect::<Vec<_>>(),
+		}),
+		staking: Some(polkadot::StakingConfig {
+			minimum_validator_count: 1,
+			validator_count: 2,
+			stakers: initial_authorities.iter()
+				.map(|x| (x.0.clone(), x.1.clone(), STASH, polkadot::StakerStatus::Validator))
+				.collect(),
+				invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+				force_era: Forcing::NotForcing,
+				slash_reward_fraction: Perbill::from_percent(10),
+				.. Default::default()
+		}),
+		elections_phragmen: Some(Default::default()),
+		democracy: Some(polkadot::DemocracyConfig::default()),
+		collective_Instance1: Some(polkadot::CouncilConfig {
+			members: vec![],
+			phantom: Default::default(),
+		}),
+		collective_Instance2: Some(polkadot::TechnicalCommitteeConfig {
+			members: vec![],
+			phantom: Default::default(),
+		}),
+		membership_Instance1: Some(Default::default()),
+		babe: Some(Default::default()),
+		grandpa: Some(Default::default()),
+		im_online: Some(Default::default()),
+		authority_discovery: Some(polkadot::AuthorityDiscoveryConfig {
+			keys: vec![],
+		}),
+		parachains: Some(polkadot::ParachainsConfig {
+			authorities: vec![],
+		}),
+		registrar: Some(polkadot::RegistrarConfig{
+			parachains: vec![],
+			_phdata: Default::default(),
+		}),
+		claims: Some(polkadot::ClaimsConfig {
+			claims: vec![],
+			vesting: vec![],
+		}),
+		vesting: Some(polkadot::VestingConfig {
+			vesting: vec![],
+		}),
+		sudo: Some(polkadot::SudoConfig {
+			key: root_key,
+		}),
+	}
+}
+
+/// Helper function to create kusama GenesisConfig for testing
+pub fn kusama_testnet_genesis(
+	initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId, ImOnlineId, ValidatorId, AuthorityDiscoveryId)>,
+	_root_key: AccountId,
+	endowed_accounts: Option<Vec<AccountId>>,
+) -> kusama::GenesisConfig {
+	let endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(testnet_accounts);
+
+	const ENDOWMENT: u128 = 1_000_000 * KSM;
+	const STASH: u128 = 100 * KSM;
+
+	kusama::GenesisConfig {
+		system: Some(kusama::SystemConfig {
+			code: kusama::WASM_BINARY.to_vec(),
+			changes_trie_config: Default::default(),
+		}),
+		indices: Some(kusama::IndicesConfig {
+			indices: vec![],
+		}),
+		balances: Some(kusama::BalancesConfig {
+			balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(),
+		}),
+		session: Some(kusama::SessionConfig {
+			keys: initial_authorities.iter().map(|x| (
+				x.0.clone(),
+				x.0.clone(),
+				kusama_session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone(), x.6.clone()),
+			)).collect::<Vec<_>>(),
+		}),
+		staking: Some(kusama::StakingConfig {
+			minimum_validator_count: 1,
+			validator_count: 2,
+			stakers: initial_authorities.iter()
+				.map(|x| (x.0.clone(), x.1.clone(), STASH, kusama::StakerStatus::Validator))
+				.collect(),
+			invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+			force_era: Forcing::NotForcing,
+			slash_reward_fraction: Perbill::from_percent(10),
+			.. Default::default()
+		}),
+		elections_phragmen: Some(Default::default()),
+		democracy: Some(kusama::DemocracyConfig::default()),
+		collective_Instance1: Some(kusama::CouncilConfig {
+			members: vec![],
+			phantom: Default::default(),
+		}),
+		collective_Instance2: Some(kusama::TechnicalCommitteeConfig {
+			members: vec![],
+			phantom: Default::default(),
+		}),
+		membership_Instance1: Some(Default::default()),
+		babe: Some(Default::default()),
+		grandpa: Some(Default::default()),
+		im_online: Some(Default::default()),
+		authority_discovery: Some(kusama::AuthorityDiscoveryConfig {
+			keys: vec![],
+		}),
+		parachains: Some(kusama::ParachainsConfig {
+			authorities: vec![],
+		}),
+		registrar: Some(kusama::RegistrarConfig{
+			parachains: vec![],
+			_phdata: Default::default(),
+		}),
+		claims: Some(kusama::ClaimsConfig {
+			claims: vec![],
+			vesting: vec![],
+		}),
+		vesting: Some(kusama::VestingConfig {
+			vesting: vec![],
+		}),
+	}
+}
+
+/// Helper function to create polkadot GenesisConfig for testing
+pub fn westend_testnet_genesis(
+	initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId, ImOnlineId, ValidatorId, AuthorityDiscoveryId)>,
+	root_key: AccountId,
+	endowed_accounts: Option<Vec<AccountId>>,
+) -> westend::GenesisConfig {
+	let endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(testnet_accounts);
+
+	const ENDOWMENT: u128 = 1_000_000 * DOTS;
+	const STASH: u128 = 100 * DOTS;
+
+	westend::GenesisConfig {
+		system: Some(westend::SystemConfig {
+			code: westend::WASM_BINARY.to_vec(),
+			changes_trie_config: Default::default(),
+		}),
+		indices: Some(westend::IndicesConfig {
+			indices: vec![],
+		}),
+		balances: Some(westend::BalancesConfig {
+			balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(),
+		}),
+		session: Some(westend::SessionConfig {
+			keys: initial_authorities.iter().map(|x| (
+				x.0.clone(),
+				x.0.clone(),
+				westend_session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone(), x.6.clone()),
+			)).collect::<Vec<_>>(),
+		}),
+		staking: Some(westend::StakingConfig {
+			minimum_validator_count: 1,
+			validator_count: 2,
+			stakers: initial_authorities.iter()
+				.map(|x| (x.0.clone(), x.1.clone(), STASH, westend::StakerStatus::Validator))
+				.collect(),
+			invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+			force_era: Forcing::NotForcing,
+			slash_reward_fraction: Perbill::from_percent(10),
+			.. Default::default()
+		}),
+		babe: Some(Default::default()),
+		grandpa: Some(Default::default()),
+		im_online: Some(Default::default()),
+		authority_discovery: Some(westend::AuthorityDiscoveryConfig {
+			keys: vec![],
+		}),
+		parachains: Some(westend::ParachainsConfig {
+			authorities: vec![],
+		}),
+		registrar: Some(westend::RegistrarConfig{
+			parachains: vec![],
+			_phdata: Default::default(),
+		}),
+		vesting: Some(westend::VestingConfig {
+			vesting: vec![],
+		}),
+		sudo: Some(westend::SudoConfig {
+			key: root_key,
+		}),
+	}
+}
+
+fn polkadot_development_config_genesis() -> polkadot::GenesisConfig {
+	polkadot_testnet_genesis(
+		vec![
+			get_authority_keys_from_seed("Alice"),
+		],
+		get_account_id_from_seed::<sr25519::Public>("Alice"),
+		None,
+	)
+}
+
+fn kusama_development_config_genesis() -> kusama::GenesisConfig {
+	kusama_testnet_genesis(
+		vec![
+			get_authority_keys_from_seed("Alice"),
+		],
+		get_account_id_from_seed::<sr25519::Public>("Alice"),
+		None,
+	)
+}
+
+fn westend_development_config_genesis() -> westend::GenesisConfig {
+	westend_testnet_genesis(
+		vec![
+			get_authority_keys_from_seed("Alice"),
+		],
+		get_account_id_from_seed::<sr25519::Public>("Alice"),
+		None,
+	)
+}
+
+/// Polkadot development config (single validator Alice)
+pub fn polkadot_development_config() -> PolkadotChainSpec {
+	PolkadotChainSpec::from_genesis(
+		"Development",
+		"dev",
+		ChainType::Development,
+		polkadot_development_config_genesis,
+		vec![],
+		None,
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
+
+/// Kusama development config (single validator Alice)
+pub fn kusama_development_config() -> KusamaChainSpec {
+	KusamaChainSpec::from_genesis(
+		"Development",
+		"kusama_dev",
+		ChainType::Development,
+		kusama_development_config_genesis,
+		vec![],
+		None,
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
+
+/// Westend development config (single validator Alice)
+pub fn westend_development_config() -> WestendChainSpec {
+	WestendChainSpec::from_genesis(
+		"Development",
+		"westend_dev",
+		ChainType::Development,
+		westend_development_config_genesis,
+		vec![],
+		None,
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
+
+fn polkadot_local_testnet_genesis() -> polkadot::GenesisConfig {
+	polkadot_testnet_genesis(
+		vec![
+			get_authority_keys_from_seed("Alice"),
+			get_authority_keys_from_seed("Bob"),
+		],
+		get_account_id_from_seed::<sr25519::Public>("Alice"),
+		None,
+	)
+}
+
+/// Polkadot local testnet config (multivalidator Alice + Bob)
+pub fn polkadot_local_testnet_config() -> PolkadotChainSpec {
+	PolkadotChainSpec::from_genesis(
+		"Local Testnet",
+		"local_testnet",
+		ChainType::Local,
+		polkadot_local_testnet_genesis,
+		vec![],
+		None,
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
+
+fn kusama_local_testnet_genesis() -> kusama::GenesisConfig {
+	kusama_testnet_genesis(
+		vec![
+			get_authority_keys_from_seed("Alice"),
+			get_authority_keys_from_seed("Bob"),
+		],
+		get_account_id_from_seed::<sr25519::Public>("Alice"),
+		None,
+	)
+}
+
+/// Kusama local testnet config (multivalidator Alice + Bob)
+pub fn kusama_local_testnet_config() -> KusamaChainSpec {
+	KusamaChainSpec::from_genesis(
+		"Kusama Local Testnet",
+		"kusama_local_testnet",
+		ChainType::Local,
+		kusama_local_testnet_genesis,
+		vec![],
+		None,
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
+
+fn westend_local_testnet_genesis() -> westend::GenesisConfig {
+	westend_testnet_genesis(
+		vec![
+			get_authority_keys_from_seed("Alice"),
+			get_authority_keys_from_seed("Bob"),
+		],
+		get_account_id_from_seed::<sr25519::Public>("Alice"),
+		None,
+	)
+}
+
+/// Westend local testnet config (multivalidator Alice + Bob)
+pub fn westend_local_testnet_config() -> WestendChainSpec {
+	WestendChainSpec::from_genesis(
+		"Westend Local Testnet",
+		"westend_local_testnet",
+		ChainType::Local,
+		westend_local_testnet_genesis,
+		vec![],
+		None,
+		Some(DEFAULT_PROTOCOL_ID),
+		None,
+		Default::default(),
+	)
+}
diff --git a/polkadot/node/service/src/client.rs b/polkadot/node/service/src/client.rs
new file mode 100644
index 00000000000..28d2bccabbe
--- /dev/null
+++ b/polkadot/node/service/src/client.rs
@@ -0,0 +1,53 @@
+// Copyright 2017-2020 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot 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.
+
+// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Polkadot Client meta trait
+
+use sp_api::{ProvideRuntimeApi, ConstructRuntimeApi, CallApiAt};
+use sp_blockchain::HeaderBackend;
+use sp_runtime::traits::Block as BlockT;
+use sc_client_api::{Backend as BackendT, BlockchainEvents};
+
+/// Polkadot client abstraction, this super trait only pulls in functionality required for
+/// polkadot internal crates like polkadot-collator.
+pub trait PolkadotClient<Block, Backend, Runtime>:
+	BlockchainEvents<Block> + Sized + Send + Sync
+	+ ProvideRuntimeApi<Block, Api = Runtime::RuntimeApi>
+	+ HeaderBackend<Block>
+	+ CallApiAt<
+		Block,
+		Error = sp_blockchain::Error,
+		StateBackend = Backend ::State
+	>
+	where
+		Block: BlockT,
+		Backend: BackendT<Block>,
+		Runtime: ConstructRuntimeApi<Block, Self>
+{}
+
+impl<Block, Backend, Runtime, Client> PolkadotClient<Block, Backend, Runtime> for Client
+	where
+		Block: BlockT,
+		Runtime: ConstructRuntimeApi<Block, Self>,
+		Backend: BackendT<Block>,
+		Client: BlockchainEvents<Block> + ProvideRuntimeApi<Block, Api = Runtime::RuntimeApi> + HeaderBackend<Block>
+			+ Sized + Send + Sync
+			+ CallApiAt<
+				Block,
+				Error = sp_blockchain::Error,
+				StateBackend = Backend ::State
+			>
+{}
diff --git a/polkadot/node/service/src/grandpa_support.rs b/polkadot/node/service/src/grandpa_support.rs
new file mode 100644
index 00000000000..a875c4b45a3
--- /dev/null
+++ b/polkadot/node/service/src/grandpa_support.rs
@@ -0,0 +1,363 @@
+// Copyright 2017-2020 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot 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.
+
+// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Polkadot-specific GRANDPA integration utilities.
+
+use polkadot_primitives::Hash;
+use sp_runtime::traits::{Block as BlockT, NumberFor};
+
+/// A custom GRANDPA voting rule that "pauses" voting (i.e. keeps voting for the
+/// same last finalized block) after a given block at height `N` has been
+/// finalized and for a delay of `M` blocks, i.e. until the best block reaches
+/// `N` + `M`, the voter will keep voting for block `N`.
+pub(crate) struct PauseAfterBlockFor<N>(pub(crate) N, pub(crate) N);
+
+impl<Block, B> grandpa::VotingRule<Block, B> for PauseAfterBlockFor<NumberFor<Block>> where
+	Block: BlockT,
+	B: sp_blockchain::HeaderBackend<Block>,
+{
+	fn restrict_vote(
+		&self,
+		backend: &B,
+		base: &Block::Header,
+		best_target: &Block::Header,
+		current_target: &Block::Header,
+	) -> Option<(Block::Hash, NumberFor<Block>)> {
+		use sp_runtime::generic::BlockId;
+		use sp_runtime::traits::Header as _;
+
+		// walk backwards until we find the target block
+		let find_target = |
+			target_number: NumberFor<Block>,
+			current_header: &Block::Header
+		| {
+			let mut target_hash = current_header.hash();
+			let mut target_header = current_header.clone();
+
+			loop {
+				if *target_header.number() < target_number {
+					unreachable!(
+						"we are traversing backwards from a known block; \
+						 blocks are stored contiguously; \
+						 qed"
+					);
+				}
+
+				if *target_header.number() == target_number {
+					return Some((target_hash, target_number));
+				}
+
+				target_hash = *target_header.parent_hash();
+				target_header = backend.header(BlockId::Hash(target_hash)).ok()?
+					.expect("Header known to exist due to the existence of one of its descendents; qed");
+			}
+		};
+
+		// only restrict votes targeting a block higher than the block
+		// we've set for the pause
+		if *current_target.number() > self.0 {
+			// if we're past the pause period (i.e. `self.0 + self.1`)
+			// then we no longer need to restrict any votes
+			if *best_target.number() > self.0 + self.1 {
+				return None;
+			}
+
+			// if we've finalized the pause block, just keep returning it
+			// until best number increases enough to pass the condition above
+			if *base.number() >= self.0 {
+				return Some((base.hash(), *base.number()));
+			}
+
+			// otherwise find the target header at the pause block
+			// to vote on
+			return find_target(self.0, current_target);
+		}
+
+		None
+	}
+}
+
+/// GRANDPA hard forks due to borked migration of session keys after a runtime
+/// upgrade (at #1491596), the signalled authority set changes were invalid
+/// (blank keys) and were impossible to finalize. The authorities for these
+/// intermediary pending changes are replaced with a static list comprised of
+/// w3f validators and randomly selected validators from the latest session (at
+/// #1500988).
+pub(crate) fn kusama_hard_forks() -> Vec<(
+	grandpa_primitives::SetId,
+	(Hash, polkadot_primitives::BlockNumber),
+	grandpa_primitives::AuthorityList,
+)> {
+	use sp_core::crypto::Ss58Codec;
+	use std::str::FromStr;
+
+	let forks = vec![
+		(
+			623,
+			"01e94e1e7e9cf07b3b0bf4e1717fce7448e5563901c2ef2e3b8e9ecaeba088b1",
+			1492283,
+		),
+		(
+			624,
+			"ddc4323c5e8966844dfaa87e0c2f74ef6b43115f17bf8e4ff38845a62d02b9a9",
+			1492436,
+		),
+		(
+			625,
+			"38ba115b296663e424e32d7b1655cd795719cef4fd7d579271a6d01086cf1628",
+			1492586,
+		),
+		(
+			626,
+			"f3172b6b8497c10fc772f5dada4eeb1f4c4919c97de9de2e1a439444d5a057ff",
+			1492955,
+		),
+		(
+			627,
+			"b26526aea299e9d24af29fdacd5cf4751a663d24894e3d0a37833aa14c58424a",
+			1493338,
+		),
+		(
+			628,
+			"3980d024327d53b8d01ef0d198a052cd058dd579508d8ed6283fe3614e0a3694",
+			1493913,
+		),
+		(
+			629,
+			"31f22997a786c25ee677786373368cae6fd501fd1bc4b212b8e267235c88179d",
+			1495083,
+		),
+		(
+			630,
+			"1c65eb250cf54b466c64f1a4003d1415a7ee275e49615450c0e0525179857eef",
+			1497404,
+		),
+		(
+			631,
+			"9e44116467cc9d7e224e36487bf2cf571698cae16b25f54a7430f1278331fdd8",
+			1498598,
+		),
+	];
+
+	let authorities = vec![
+		"CwjLJ1zPWK5Ao9WChAFp7rWGEgN3AyXXjTRPrqgm5WwBpoS",
+		"Dp8FHpZTzvoKXztkfrUAkF6xNf6sjVU5ZLZ29NEGUazouou",
+		"DtK7YfkhNWU6wEPF1dShsFdhtosVAuJPLkoGhKhG1r5LjKq",
+		"FLnHYBuoyThzqJ45tdb8P6yMLdocM7ir27Pg1AnpYoygm1K",
+		"FWEfJ5UMghr52UopgYjawAg6hQg3ztbQek75pfeRtLVi8pB",
+		"ECoLHAu7HKWGTB9od82HAtequYj6hvNHigkGSB9g3ApxAwB",
+		"GL1Tg3Uppo8GYL9NjKj4dWKcS6tW98REop9G5hpu7HgFwTa",
+		"ExnjU5LZMktrgtQBE3An6FsQfvaKG1ukxPqwhJydgdgarmY",
+		"CagLpgCBu5qJqYF2tpFX6BnU4yHvMGSjc7r3Ed1jY3tMbQt",
+		"DsrtmMsD4ijh3n4uodxPoiW9NZ7v7no5wVvPVj8fL1dfrWB",
+		"HQB4EctrVR68ozZDyBiRJzLRAEGh1YKgCkAsFjJcegL9RQA",
+		"H2YTYbXTFkDY1cGnv164ecnDT3hsD2bQXtyiDbcQuXcQZUV",
+		"H5WL8jXmbkCoEcLfvqJkbLUeGrDFsJiMXkhhRWn3joct1tE",
+		"DpB37GDrJDYcmg2df2eqsrPKMay1u8hyZ6sQi2FuUiUeNLu",
+		"FR8yjKRA9MTjvFGK8kfzrdC23Fr6xd7rfBvZXSjAsmuxURE",
+		"DxHPty3B9fpj3duu6Gc6gCSCAvsydJHJEY5G3oVYT8S5BYJ",
+		"DbVKC8ZJjevrhqSnZyJMMvmPL7oPPL4ed1roxawYnHVgyin",
+		"DVJV81kab2J6oTyRJ9T3NCwW2DSrysbWCssvMcE6cwZHnAd",
+		"Fg4rDAyzoVzf39Zo8JFPo4W314ntNWNwm3shr4xKe8M1fJg",
+		"GUaNcnAruMVxHGTs7gGpSUpigRJboQYQBBQyPohkFcP6NMH",
+		"J4BMGF4W9yWiJz4pkhQW73X6QMGpKUzmPppVnqzBCqw5dQq",
+		"E1cR61L1tdDEop4WdWVqcq1H1x6VqsDpSHvFyUeC41uruVJ",
+		"GoWLzBsj1f23YtdDpyntnvN1LwXKhF5TEeZvBeTVxofgWGR",
+		"CwHwmbogSwtRbrkajVBNubPvWmHBGU4bhMido54M9CjuKZD",
+		"FLT63y9oVXJnyiWMAL4RvWxsQx21Vymw9961Z7NRFmSG7rw",
+		"FoQ2y6JuHuHTG4rHFL3f2hCxfJMvtrq8wwPWdv8tsdkcyA8",
+		"D7QQKqqs8ocGorRA12h4QoBSHDia1DkHeXT4eMfjWQ483QH",
+		"J6z7FP35F9DiiU985bhkDTS3WxyeTBeoo9MtLdLoD3GiWPj",
+		"EjapydCK25AagodRbDECavHAy8yQY1tmeRhwUXhVWx4cFPv",
+		"H8admATcRkGCrF1dTDDBCjQDsYjMkuPaN9YwR2mSCj4DWMQ",
+		"FtHMRU1fxsoswJjBvyCGvECepC7gP2X77QbNpyikYSqqR6k",
+		"DzY5gwr45GVRUFzRMmeg8iffpqYF47nm3XbJhmjG97FijaE",
+		"D3HKWAihSUmg8HrfeFrftSwNK7no261yA9RNr3LUUdsuzuJ",
+		"D82DwwGJGTcSvtB3SmNrZejnSertbPzpkYvDUp3ibScL3ne",
+		"FTPxLXLQvMDQYFA6VqNLGwWPKhemMYP791XVj8TmDpFuV3b",
+		"FzGfKmS7N8Z1tvCBU5JH1eBXZQ9pCtRNoMUnNVv38wZNq72",
+		"GDfm1MyLAQ7Rh8YPtF6FtMweV4hz91zzeDy2sSABNNqAbmg",
+		"DiVQbq7sozeKp7PXPM1HLFc2m7ih8oepKLRK99oBY3QZak1",
+		"HErWh7D2RzrjWWB2fTJfcAejD9MJpadeWWZM2Wnk7LiNWfG",
+		"Es4DbDauYZYyRJbr6VxrhdcM1iufP9GtdBYf3YtSEvdwNyb",
+		"EBgXT6FaVo4WsN2LmfnB2jnpDFf4zay3E492RGSn6v1tY99",
+		"Dr9Zg4fxZurexParztL9SezFeHsPwdP8uGgULeRMbk8DDHJ",
+		"JEnSTZJpLh91cSryptj57RtFxq9xXqf4U5wBH3qoP91ZZhN",
+		"DqtRkrmtPANa8wrYR7Ce2LxJxk2iNFtiCxv1cXbx54uqdTN",
+		"GaxmF53xbuTFKopVEseWiaCTa8fC6f99n4YfW8MGPSPYX3s",
+		"EiCesgkAaighBKMpwFSAUdvwE4mRjBjNmmd5fP6d4FG8DAx",
+		"HVbwWGUx7kCgUGap1Mfcs37g6JAZ5qsfsM7TsDRcSqvfxmd",
+		"G45bc8Ajrd6YSXav77gQwjjGoAsR2qiGd1aLzkMy7o1RLwd",
+		"Cqix2rD93Mdf7ytg8tBavAig2TvhXPgPZ2mejQvkq7qgRPq",
+		"GpodE2S5dPeVjzHB4Drm8R9rEwcQPtwAspXqCVz1ooFWf5K",
+		"CwfmfRmzPKLj3ntSCejuVwYmQ1F9iZWY4meQrAVoJ2G8Kce",
+		"Fhp5NPvutRCJ4Gx3G8vCYGaveGcU3KgTwfrn5Zr8sLSgwVx",
+		"GeYRRPkyi23wSF3cJGjq82117fKJZUbWsAGimUnzb5RPbB1",
+		"DzCJ4y5oT611dfKQwbBDVbtCfENTdMCjb4KGMU3Mq6nyUMu",
+	];
+
+	let authorities = authorities
+		.into_iter()
+		.map(|address| {
+			(
+				grandpa_primitives::AuthorityId::from_ss58check(address)
+					.expect("hard fork authority addresses are static and they should be carefully defined; qed."),
+				1,
+			)
+		})
+		.collect::<Vec<_>>();
+
+	forks
+		.into_iter()
+		.map(|(set_id, hash, number)| {
+			let hash = Hash::from_str(hash)
+				.expect("hard fork hashes are static and they should be carefully defined; qed.");
+
+			(set_id, (hash, number), authorities.clone())
+		})
+		.collect()
+}
+
+#[cfg(test)]
+mod tests {
+	use polkadot_test_runtime_client::prelude::*;
+	use polkadot_test_runtime_client::sp_consensus::BlockOrigin;
+	use sc_block_builder::BlockBuilderProvider;
+	use grandpa::VotingRule;
+	use sp_blockchain::HeaderBackend;
+	use sp_runtime::generic::BlockId;
+	use sp_runtime::traits::Header;
+	use std::sync::Arc;
+
+	#[test]
+	fn grandpa_pause_voting_rule_works() {
+		let _ = env_logger::try_init();
+
+		let client = Arc::new(polkadot_test_runtime_client::new());
+
+		let mut push_blocks = {
+			let mut client = client.clone();
+			move |n| {
+				for _ in 0..n {
+					let mut builder = client.new_block(Default::default()).unwrap();
+
+					for extrinsic in polkadot_test_runtime_client::needed_extrinsics(vec![]) {
+						builder.push(extrinsic).unwrap()
+					}
+
+					let block = builder.build().unwrap().block;
+					client.import(BlockOrigin::Own, block).unwrap();
+				}
+			}
+		};
+
+		let get_header = {
+			let client = client.clone();
+			move |n| client.header(&BlockId::Number(n)).unwrap().unwrap()
+		};
+
+		// the rule should filter all votes after block #20
+		// is finalized until block #50 is imported.
+		let voting_rule = super::PauseAfterBlockFor(20, 30);
+
+		// add 10 blocks
+		push_blocks(10);
+		assert_eq!(
+			client.info().best_number,
+			10,
+		);
+
+		// we have not reached the pause block
+		// therefore nothing should be restricted
+		assert_eq!(
+			voting_rule.restrict_vote(
+				&*client,
+				&get_header(0),
+				&get_header(10),
+				&get_header(10),
+			),
+			None,
+		);
+
+		// add 15 more blocks
+		// best block: #25
+		push_blocks(15);
+
+		// we are targeting the pause block,
+		// the vote should not be restricted
+		assert_eq!(
+			voting_rule.restrict_vote(
+				&*client,
+				&get_header(10),
+				&get_header(20),
+				&get_header(20),
+			),
+			None,
+		);
+
+		// we are past the pause block, votes should
+		// be limited to the pause block.
+		let pause_block = get_header(20);
+		assert_eq!(
+			voting_rule.restrict_vote(
+				&*client,
+				&get_header(10),
+				&get_header(21),
+				&get_header(21),
+			),
+			Some((pause_block.hash(), *pause_block.number())),
+		);
+
+		// we've finalized the pause block, so we'll keep
+		// restricting our votes to it.
+		assert_eq!(
+			voting_rule.restrict_vote(
+				&*client,
+				&pause_block, // #20
+				&get_header(21),
+				&get_header(21),
+			),
+			Some((pause_block.hash(), *pause_block.number())),
+		);
+
+		// add 30 more blocks
+		// best block: #55
+		push_blocks(30);
+
+		// we're at the last block of the pause, this block
+		// should still be considered in the pause period
+		assert_eq!(
+			voting_rule.restrict_vote(
+				&*client,
+				&pause_block, // #20
+				&get_header(50),
+				&get_header(50),
+			),
+			Some((pause_block.hash(), *pause_block.number())),
+		);
+
+		// we're past the pause period, no votes should be filtered
+		assert_eq!(
+			voting_rule.restrict_vote(
+				&*client,
+				&pause_block, // #20
+				&get_header(51),
+				&get_header(51),
+			),
+			None,
+		);
+	}
+}
diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs
new file mode 100644
index 00000000000..af517adc0e9
--- /dev/null
+++ b/polkadot/node/service/src/lib.rs
@@ -0,0 +1,729 @@
+// Copyright 2017-2020 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot 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.
+
+// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Polkadot service. Specialized wrapper over substrate service.
+
+pub mod chain_spec;
+mod grandpa_support;
+mod client;
+
+use std::sync::Arc;
+use std::time::Duration;
+use polkadot_primitives::{parachain, AccountId, Nonce, Balance};
+#[cfg(feature = "full-node")]
+use service::{error::Error as ServiceError, ServiceBuilder};
+use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider};
+use sc_executor::native_executor_instance;
+use log::info;
+use sp_blockchain::HeaderBackend;
+use polkadot_overseer::{
+	self as overseer,
+	BlockInfo, Overseer, OverseerHandler, Subsystem, SubsystemContext, SpawnedSubsystem,
+	ValidationSubsystemMessage, CandidateBackingSubsystemMessage,
+};
+pub use service::{
+	AbstractService, Role, PruningMode, TransactionPoolOptions, Error, RuntimeGenesis,
+	TFullClient, TLightClient, TFullBackend, TLightBackend, TFullCallExecutor, TLightCallExecutor,
+	Configuration, ChainSpec, ServiceBuilderCommand,
+};
+pub use service::config::{DatabaseConfig, PrometheusConfig};
+pub use sc_executor::NativeExecutionDispatch;
+pub use sc_client_api::{Backend, ExecutionStrategy, CallExecutor};
+pub use sc_consensus::LongestChain;
+pub use sp_api::{ApiRef, Core as CoreApi, ConstructRuntimeApi, ProvideRuntimeApi, StateBackend};
+pub use sp_runtime::traits::{DigestFor, HashFor, NumberFor};
+pub use consensus_common::{Proposal, SelectChain, BlockImport, RecordProof, block_validation::Chain};
+pub use polkadot_primitives::parachain::{CollatorId, ParachainHost};
+pub use polkadot_primitives::{Block, BlockId};
+pub use sp_runtime::traits::{Block as BlockT, self as runtime_traits, BlakeTwo256};
+pub use chain_spec::{PolkadotChainSpec, KusamaChainSpec, WestendChainSpec};
+#[cfg(feature = "full-node")]
+pub use codec::Codec;
+pub use polkadot_runtime;
+pub use kusama_runtime;
+pub use westend_runtime;
+use prometheus_endpoint::Registry;
+pub use self::client::PolkadotClient;
+
+native_executor_instance!(
+	pub PolkadotExecutor,
+	polkadot_runtime::api::dispatch,
+	polkadot_runtime::native_version,
+	frame_benchmarking::benchmarking::HostFunctions,
+);
+
+native_executor_instance!(
+	pub KusamaExecutor,
+	kusama_runtime::api::dispatch,
+	kusama_runtime::native_version,
+	frame_benchmarking::benchmarking::HostFunctions,
+);
+
+native_executor_instance!(
+	pub WestendExecutor,
+	westend_runtime::api::dispatch,
+	westend_runtime::native_version,
+	frame_benchmarking::benchmarking::HostFunctions,
+);
+
+/// A set of APIs that polkadot-like runtimes must implement.
+pub trait RuntimeApiCollection<Extrinsic: codec::Codec + Send + Sync + 'static>:
+	sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
+	+ sp_api::ApiExt<Block, Error = sp_blockchain::Error>
+	+ babe_primitives::BabeApi<Block>
+	+ grandpa_primitives::GrandpaApi<Block>
+	+ ParachainHost<Block>
+	+ sp_block_builder::BlockBuilder<Block>
+	+ system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce>
+	+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance, Extrinsic>
+	+ sp_api::Metadata<Block>
+	+ sp_offchain::OffchainWorkerApi<Block>
+	+ sp_session::SessionKeys<Block>
+	+ authority_discovery_primitives::AuthorityDiscoveryApi<Block>
+where
+	Extrinsic: RuntimeExtrinsic,
+	<Self as sp_api::ApiExt<Block>>::StateBackend: sp_api::StateBackend<BlakeTwo256>,
+{}
+
+impl<Api, Extrinsic> RuntimeApiCollection<Extrinsic> for Api
+where
+	Api:
+	sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
+	+ sp_api::ApiExt<Block, Error = sp_blockchain::Error>
+	+ babe_primitives::BabeApi<Block>
+	+ grandpa_primitives::GrandpaApi<Block>
+	+ ParachainHost<Block>
+	+ sp_block_builder::BlockBuilder<Block>
+	+ system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce>
+	+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance, Extrinsic>
+	+ sp_api::Metadata<Block>
+	+ sp_offchain::OffchainWorkerApi<Block>
+	+ sp_session::SessionKeys<Block>
+	+ authority_discovery_primitives::AuthorityDiscoveryApi<Block>,
+	Extrinsic: RuntimeExtrinsic,
+	<Self as sp_api::ApiExt<Block>>::StateBackend: sp_api::StateBackend<BlakeTwo256>,
+{}
+
+pub trait RuntimeExtrinsic: codec::Codec + Send + Sync + 'static {}
+
+impl<E> RuntimeExtrinsic for E where E: codec::Codec + Send + Sync + 'static {}
+
+/// Can be called for a `Configuration` to check if it is a configuration for the `Kusama` network.
+pub trait IdentifyVariant {
+	/// Returns if this is a configuration for the `Kusama` network.
+	fn is_kusama(&self) -> bool;
+
+	/// Returns if this is a configuration for the `Westend` network.
+	fn is_westend(&self) -> bool;
+}
+
+impl IdentifyVariant for Box<dyn ChainSpec> {
+	fn is_kusama(&self) -> bool {
+		self.id().starts_with("kusama") || self.id().starts_with("ksm")
+	}
+	fn is_westend(&self) -> bool {
+		self.id().starts_with("westend") || self.id().starts_with("wnd")
+	}
+}
+
+// If we're using prometheus, use a registry with a prefix of `polkadot`.
+fn set_prometheus_registry(config: &mut Configuration) -> Result<(), ServiceError> {
+	if let Some(PrometheusConfig { registry, .. }) = config.prometheus_config.as_mut() {
+		*registry = Registry::new_custom(Some("polkadot".into()), None)?;
+	}
+
+	Ok(())
+}
+
+/// Starts a `ServiceBuilder` for a full service.
+///
+/// Use this macro if you don't actually need the full service, but just the builder in order to
+/// be able to perform chain operations.
+macro_rules! new_full_start {
+	($config:expr, $runtime:ty, $executor:ty, $informant_prefix:expr $(,)?) => {{
+		set_prometheus_registry(&mut $config)?;
+
+		let mut import_setup = None;
+		let mut rpc_setup = None;
+		let inherent_data_providers = inherents::InherentDataProviders::new();
+		let builder = service::ServiceBuilder::new_full::<
+			Block, $runtime, $executor
+		>($config)?
+			.with_informant_prefix($informant_prefix.unwrap_or_default())?
+			.with_select_chain(|_, backend| {
+				Ok(sc_consensus::LongestChain::new(backend.clone()))
+			})?
+			.with_transaction_pool(|builder| {
+				let pool_api = sc_transaction_pool::FullChainApi::new(builder.client().clone());
+				let pool = sc_transaction_pool::BasicPool::new(
+					builder.config().transaction_pool.clone(),
+					std::sync::Arc::new(pool_api),
+					builder.prometheus_registry(),
+				);
+				Ok(pool)
+			})?
+			.with_import_queue(|
+				config,
+				client,
+				mut select_chain,
+				_,
+				spawn_task_handle,
+				registry,
+			| {
+				let select_chain = select_chain.take()
+					.ok_or_else(|| service::Error::SelectChainRequired)?;
+
+				let grandpa_hard_forks = if config.chain_spec.is_kusama() {
+					grandpa_support::kusama_hard_forks()
+				} else {
+					Vec::new()
+				};
+
+				let (grandpa_block_import, grandpa_link) =
+					grandpa::block_import_with_authority_set_hard_forks(
+						client.clone(),
+						&(client.clone() as Arc<_>),
+						select_chain,
+						grandpa_hard_forks,
+					)?;
+
+				let justification_import = grandpa_block_import.clone();
+
+				let (block_import, babe_link) = babe::block_import(
+					babe::Config::get_or_compute(&*client)?,
+					grandpa_block_import,
+					client.clone(),
+				)?;
+
+				let import_queue = babe::import_queue(
+					babe_link.clone(),
+					block_import.clone(),
+					Some(Box::new(justification_import)),
+					None,
+					client,
+					inherent_data_providers.clone(),
+					spawn_task_handle,
+					registry,
+				)?;
+
+				import_setup = Some((block_import, grandpa_link, babe_link));
+				Ok(import_queue)
+			})?
+			.with_rpc_extensions_builder(|builder| {
+				let grandpa_link = import_setup.as_ref().map(|s| &s.1)
+					.expect("GRANDPA LinkHalf is present for full services or set up failed; qed.");
+
+				let shared_authority_set = grandpa_link.shared_authority_set().clone();
+				let shared_voter_state = grandpa::SharedVoterState::empty();
+
+				rpc_setup = Some((shared_voter_state.clone()));
+
+				let babe_link = import_setup.as_ref().map(|s| &s.2)
+					.expect("BabeLink is present for full services or set up faile; qed.");
+
+				let babe_config = babe_link.config().clone();
+				let shared_epoch_changes = babe_link.epoch_changes().clone();
+
+				let client = builder.client().clone();
+				let pool = builder.pool().clone();
+				let select_chain = builder.select_chain().cloned()
+					.expect("SelectChain is present for full services or set up failed; qed.");
+				let keystore = builder.keystore().clone();
+
+				Ok(move |deny_unsafe| -> polkadot_rpc::RpcExtension {
+					let deps = polkadot_rpc::FullDeps {
+						client: client.clone(),
+						pool: pool.clone(),
+						select_chain: select_chain.clone(),
+						deny_unsafe,
+						babe: polkadot_rpc::BabeDeps {
+							babe_config: babe_config.clone(),
+							shared_epoch_changes: shared_epoch_changes.clone(),
+							keystore: keystore.clone(),
+						},
+						grandpa: polkadot_rpc::GrandpaDeps {
+							shared_voter_state: shared_voter_state.clone(),
+							shared_authority_set: shared_authority_set.clone(),
+						},
+					};
+
+					polkadot_rpc::create_full(deps)
+				})
+			})?;
+
+		(builder, import_setup, inherent_data_providers, rpc_setup)
+	}}
+}
+
+struct ValidationSubsystem;
+
+impl Subsystem<ValidationSubsystemMessage> for ValidationSubsystem {
+	fn start(&mut self, mut ctx: SubsystemContext<ValidationSubsystemMessage>) -> SpawnedSubsystem {
+		SpawnedSubsystem(Box::pin(async move {
+			while let Ok(_) = ctx.recv().await {}
+		}))
+	}
+}
+
+struct CandidateBackingSubsystem;
+
+impl Subsystem<CandidateBackingSubsystemMessage> for CandidateBackingSubsystem {
+	fn start(&mut self, mut ctx: SubsystemContext<CandidateBackingSubsystemMessage>) -> SpawnedSubsystem {
+		SpawnedSubsystem(Box::pin(async move {
+			while let Ok(_) = ctx.recv().await {}
+		}))
+	}
+}
+
+fn real_overseer<S: futures::task::Spawn>(
+	leaves: impl IntoIterator<Item = BlockInfo>,
+	s: S,
+) -> Result<(Overseer<S>, OverseerHandler), ServiceError> {
+	let validation = Box::new(ValidationSubsystem);
+	let candidate_backing = Box::new(CandidateBackingSubsystem);
+	Overseer::new(leaves, validation, candidate_backing, s)
+		.map_err(|e| ServiceError::Other(format!("Failed to create an Overseer: {:?}", e)))
+}
+
+/// Builds a new service for a full client.
+#[macro_export]
+macro_rules! new_full {
+	(
+		$config:expr,
+		$collating_for:expr,
+		$authority_discovery_enabled:expr,
+		$grandpa_pause:expr,
+		$runtime:ty,
+		$dispatch:ty,
+		$informant_prefix:expr $(,)?
+	) => {{
+		use sc_client_api::ExecutorProvider;
+		use sp_core::traits::BareCryptoStorePtr;
+
+		let is_collator = $collating_for.is_some();
+		let role = $config.role.clone();
+		let is_authority = role.is_authority() && !is_collator;
+		let force_authoring = $config.force_authoring;
+		let disable_grandpa = $config.disable_grandpa;
+		let name = $config.network.node_name.clone();
+
+		let (builder, mut import_setup, inherent_data_providers, mut rpc_setup) =
+			new_full_start!($config, $runtime, $dispatch, $informant_prefix);
+
+		let service = builder
+			.with_finality_proof_provider(|client, backend| {
+				let provider = client as Arc<dyn grandpa::StorageAndProofProvider<_, _>>;
+				Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, provider)) as _)
+			})?
+			.build_full()?;
+
+		let (block_import, link_half, babe_link) = import_setup.take()
+			.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
+
+		let shared_voter_state = rpc_setup.take()
+			.expect("The SharedVoterState is present for Full Services or setup failed before. qed");
+
+		let client = service.client();
+
+		let overseer_client = service.client();
+		let spawner = service.spawn_task_handle();
+		let leaves: Vec<_> = service.select_chain().ok_or(ServiceError::SelectChainRequired)?
+			.leaves()
+			.unwrap_or_else(|_| vec![])
+			.into_iter()
+			.filter_map(|hash| {
+				let number = client.number(hash).ok()??;
+				let parent_hash = client.header(&BlockId::Hash(hash)).ok()??.parent_hash;
+
+				Some(BlockInfo {
+					hash,
+					parent_hash,
+					number,
+				})
+			})
+			.collect();
+
+		let (overseer, handler) = real_overseer(leaves, spawner)?;
+
+		service.spawn_essential_task("overseer", Box::pin(async move {
+			use futures::{pin_mut, select, FutureExt};
+
+			let forward = overseer::forward_events(overseer_client, handler);
+
+			let forward = forward.fuse();
+			let overseer_fut = overseer.run().fuse();
+
+			pin_mut!(overseer_fut);
+			pin_mut!(forward);
+
+			loop {
+				select! {
+					_ = forward => break,
+					_ = overseer_fut => break,
+					complete => break,
+				}
+			}
+		}));
+
+		if role.is_authority() {
+			let select_chain = service.select_chain().ok_or(ServiceError::SelectChainRequired)?;
+			let can_author_with =
+				consensus_common::CanAuthorWithNativeVersion::new(client.executor().clone());
+
+			// TODO: custom proposer (https://github.com/paritytech/polkadot/issues/1248)
+			let proposer = sc_basic_authorship::ProposerFactory::new(
+				client.clone(),
+				service.transaction_pool(),
+				None,
+			);
+
+			let babe_config = babe::BabeParams {
+				keystore: service.keystore(),
+				client: client.clone(),
+				select_chain,
+				block_import,
+				env: proposer,
+				sync_oracle: service.network(),
+				inherent_data_providers: inherent_data_providers.clone(),
+				force_authoring,
+				babe_link,
+				can_author_with,
+			};
+
+			let babe = babe::start_babe(babe_config)?;
+			service.spawn_essential_task("babe", babe);
+		}
+
+		// if the node isn't actively participating in consensus then it doesn't
+		// need a keystore, regardless of which protocol we use below.
+		let keystore = if is_authority {
+			Some(service.keystore() as BareCryptoStorePtr)
+		} else {
+			None
+		};
+
+		let config = grandpa::Config {
+			// FIXME substrate#1578 make this available through chainspec
+			gossip_duration: Duration::from_millis(1000),
+			justification_period: 512,
+			name: Some(name),
+			observer_enabled: false,
+			keystore,
+			is_authority: role.is_network_authority(),
+		};
+
+		let enable_grandpa = !disable_grandpa;
+		if enable_grandpa {
+			// start the full GRANDPA voter
+			// NOTE: unlike in substrate we are currently running the full
+			// GRANDPA voter protocol for all full nodes (regardless of whether
+			// they're validators or not). at this point the full voter should
+			// provide better guarantees of block and vote data availability than
+			// the observer.
+
+			// add a custom voting rule to temporarily stop voting for new blocks
+			// after the given pause block is finalized and restarting after the
+			// given delay.
+			let voting_rule = match $grandpa_pause {
+				Some((block, delay)) => {
+					info!("GRANDPA scheduled voting pause set for block #{} with a duration of {} blocks.",
+						block,
+						delay,
+					);
+
+					grandpa::VotingRulesBuilder::default()
+						.add(grandpa_support::PauseAfterBlockFor(block, delay))
+						.build()
+				},
+				None =>
+					grandpa::VotingRulesBuilder::default()
+						.build(),
+			};
+
+			let grandpa_config = grandpa::GrandpaParams {
+				config,
+				link: link_half,
+				network: service.network(),
+				inherent_data_providers: inherent_data_providers.clone(),
+				telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
+				voting_rule,
+				prometheus_registry: service.prometheus_registry(),
+				shared_voter_state,
+			};
+
+			service.spawn_essential_task(
+				"grandpa-voter",
+				grandpa::run_grandpa_voter(grandpa_config)?
+			);
+		} else {
+			grandpa::setup_disabled_grandpa(
+				client.clone(),
+				&inherent_data_providers,
+				service.network(),
+			)?;
+		}
+
+		(service, client)
+	}}
+}
+
+pub struct FullNodeHandles;
+
+/// Builds a new service for a light client.
+#[macro_export]
+macro_rules! new_light {
+	($config:expr, $runtime:ty, $dispatch:ty) => {{
+		crate::set_prometheus_registry(&mut $config)?;
+		let inherent_data_providers = inherents::InherentDataProviders::new();
+
+		ServiceBuilder::new_light::<Block, $runtime, $dispatch>($config)?
+			.with_select_chain(|_, backend| {
+				Ok(sc_consensus::LongestChain::new(backend.clone()))
+			})?
+			.with_transaction_pool(|builder| {
+				let fetcher = builder.fetcher()
+					.ok_or_else(|| "Trying to start light transaction pool without active fetcher")?;
+				let pool_api = sc_transaction_pool::LightChainApi::new(
+					builder.client().clone(),
+					fetcher,
+				);
+				let pool = sc_transaction_pool::BasicPool::with_revalidation_type(
+					builder.config().transaction_pool.clone(),
+					Arc::new(pool_api),
+					builder.prometheus_registry(),
+					sc_transaction_pool::RevalidationType::Light,
+				);
+				Ok(pool)
+			})?
+			.with_import_queue_and_fprb(|
+				_config,
+				client,
+				backend,
+				fetcher,
+				_select_chain,
+				_,
+				spawn_task_handle,
+				registry,
+			| {
+				let fetch_checker = fetcher
+					.map(|fetcher| fetcher.checker().clone())
+					.ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
+				let grandpa_block_import = grandpa::light_block_import(
+					client.clone(), backend, &(client.clone() as Arc<_>), Arc::new(fetch_checker)
+				)?;
+
+				let finality_proof_import = grandpa_block_import.clone();
+				let finality_proof_request_builder =
+					finality_proof_import.create_finality_proof_request_builder();
+
+				let (babe_block_import, babe_link) = babe::block_import(
+					babe::Config::get_or_compute(&*client)?,
+					grandpa_block_import,
+					client.clone(),
+				)?;
+
+				// FIXME: pruning task isn't started since light client doesn't do `AuthoritySetup`.
+				let import_queue = babe::import_queue(
+					babe_link,
+					babe_block_import,
+					None,
+					Some(Box::new(finality_proof_import)),
+					client,
+					inherent_data_providers.clone(),
+					spawn_task_handle,
+					registry,
+				)?;
+
+				Ok((import_queue, finality_proof_request_builder))
+			})?
+			.with_finality_proof_provider(|client, backend| {
+				let provider = client as Arc<dyn grandpa::StorageAndProofProvider<_, _>>;
+				Ok(Arc::new(grandpa::FinalityProofProvider::new(backend, provider)) as _)
+			})?
+			.with_rpc_extensions(|builder| {
+				let fetcher = builder.fetcher()
+					.ok_or_else(|| "Trying to start node RPC without active fetcher")?;
+				let remote_blockchain = builder.remote_backend()
+					.ok_or_else(|| "Trying to start node RPC without active remote blockchain")?;
+
+				let light_deps = polkadot_rpc::LightDeps {
+					remote_blockchain,
+					fetcher,
+					client: builder.client().clone(),
+					pool: builder.pool(),
+				};
+				Ok(polkadot_rpc::create_light(light_deps))
+			})?
+			.build_light()
+	}}
+}
+
+/// Builds a new object suitable for chain operations.
+pub fn new_chain_ops<Runtime, Dispatch, Extrinsic>(mut config: Configuration)
+	-> Result<impl ServiceBuilderCommand<Block=Block>, ServiceError>
+where
+	Runtime: ConstructRuntimeApi<Block, service::TFullClient<Block, Runtime, Dispatch>> + Send + Sync + 'static,
+	Runtime::RuntimeApi:
+	RuntimeApiCollection<Extrinsic, StateBackend = sc_client_api::StateBackendFor<TFullBackend<Block>, Block>>,
+	Dispatch: NativeExecutionDispatch + 'static,
+	Extrinsic: RuntimeExtrinsic,
+	<Runtime::RuntimeApi as sp_api::ApiExt<Block>>::StateBackend: sp_api::StateBackend<BlakeTwo256>,
+{
+	config.keystore = service::config::KeystoreConfig::InMemory;
+	Ok(new_full_start!(config, Runtime, Dispatch, None).0)
+}
+
+/// Create a new Polkadot service for a full node.
+#[cfg(feature = "full-node")]
+pub fn polkadot_new_full(
+	mut config: Configuration,
+	collating_for: Option<(CollatorId, parachain::Id)>,
+	_max_block_data_size: Option<u64>,
+	_authority_discovery_enabled: bool,
+	_slot_duration: u64,
+	grandpa_pause: Option<(u32, u32)>,
+	informant_prefix: Option<String>,
+)
+	-> Result<(
+		impl AbstractService,
+		Arc<impl PolkadotClient<
+			Block,
+			TFullBackend<Block>,
+			polkadot_runtime::RuntimeApi
+		>>,
+		FullNodeHandles,
+	), ServiceError>
+{
+	let (service, client) = new_full!(
+		config,
+		collating_for,
+		authority_discovery_enabled,
+		grandpa_pause,
+		polkadot_runtime::RuntimeApi,
+		PolkadotExecutor,
+		informant_prefix,
+	);
+
+	Ok((service, client, FullNodeHandles))
+}
+
+/// Create a new Kusama service for a full node.
+#[cfg(feature = "full-node")]
+pub fn kusama_new_full(
+	mut config: Configuration,
+	collating_for: Option<(CollatorId, parachain::Id)>,
+	_max_block_data_size: Option<u64>,
+	_authority_discovery_enabled: bool,
+	_slot_duration: u64,
+	grandpa_pause: Option<(u32, u32)>,
+	informant_prefix: Option<String>,
+) -> Result<(
+		impl AbstractService,
+		Arc<impl PolkadotClient<
+			Block,
+			TFullBackend<Block>,
+			kusama_runtime::RuntimeApi
+			>
+		>,
+		FullNodeHandles,
+	), ServiceError>
+{
+	let (service, client) = new_full!(
+		config,
+		collating_for,
+		authority_discovery_enabled,
+		grandpa_pause,
+		kusama_runtime::RuntimeApi,
+		KusamaExecutor,
+		informant_prefix,
+	);
+
+	Ok((service, client, FullNodeHandles))
+}
+
+/// Create a new Kusama service for a full node.
+#[cfg(feature = "full-node")]
+pub fn westend_new_full(
+	mut config: Configuration,
+	collating_for: Option<(CollatorId, parachain::Id)>,
+	_max_block_data_size: Option<u64>,
+	_authority_discovery_enabled: bool,
+	_slot_duration: u64,
+	grandpa_pause: Option<(u32, u32)>,
+	informant_prefix: Option<String>,
+)
+	-> Result<(
+		impl AbstractService,
+		Arc<impl PolkadotClient<
+			Block,
+			TFullBackend<Block>,
+			westend_runtime::RuntimeApi
+		>>,
+		FullNodeHandles,
+	), ServiceError>
+{
+	let (service, client) = new_full!(
+		config,
+		collating_for,
+		authority_discovery_enabled,
+		grandpa_pause,
+		westend_runtime::RuntimeApi,
+		WestendExecutor,
+		informant_prefix,
+	);
+
+	Ok((service, client, FullNodeHandles))
+}
+
+/// Create a new Polkadot service for a light client.
+pub fn polkadot_new_light(mut config: Configuration) -> Result<
+	impl AbstractService<
+		Block = Block,
+		RuntimeApi = polkadot_runtime::RuntimeApi,
+		Backend = TLightBackend<Block>,
+		SelectChain = LongestChain<TLightBackend<Block>, Block>,
+		CallExecutor = TLightCallExecutor<Block, PolkadotExecutor>,
+	>, ServiceError>
+{
+	new_light!(config, polkadot_runtime::RuntimeApi, PolkadotExecutor)
+}
+
+/// Create a new Kusama service for a light client.
+pub fn kusama_new_light(mut config: Configuration) -> Result<
+	impl AbstractService<
+		Block = Block,
+		RuntimeApi = kusama_runtime::RuntimeApi,
+		Backend = TLightBackend<Block>,
+		SelectChain = LongestChain<TLightBackend<Block>, Block>,
+		CallExecutor = TLightCallExecutor<Block, KusamaExecutor>,
+	>, ServiceError>
+{
+	new_light!(config, kusama_runtime::RuntimeApi, KusamaExecutor)
+}
+
+/// Create a new Westend service for a light client.
+pub fn westend_new_light(mut config: Configuration, ) -> Result<
+	impl AbstractService<
+		Block = Block,
+		RuntimeApi = westend_runtime::RuntimeApi,
+		Backend = TLightBackend<Block>,
+		SelectChain = LongestChain<TLightBackend<Block>, Block>,
+		CallExecutor = TLightCallExecutor<Block, KusamaExecutor>
+	>,
+	ServiceError>
+{
+	new_light!(config, westend_runtime::RuntimeApi, KusamaExecutor)
+}
-- 
GitLab