From 9b6347175ba5f2401c2f9898848cff220045b646 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov 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 * Remove duplicate json files Co-authored-by: Robert Habermeier --- Cargo.lock | 62 ++ Cargo.toml | 7 + cli/Cargo.toml | 11 +- cli/src/command.rs | 5 +- cli/src/lib.rs | 8 + collator/Cargo.toml | 8 +- collator/src/lib.rs | 48 +- node/overseer/src/lib.rs | 3 +- node/service/Cargo.toml | 69 ++ node/service/README.adoc | 5 + node/service/src/chain_spec.rs | 963 ++++++++++++++++++++++++++++ node/service/src/client.rs | 53 ++ node/service/src/grandpa_support.rs | 363 +++++++++++ node/service/src/lib.rs | 729 +++++++++++++++++++++ 14 files changed, 2325 insertions(+), 9 deletions(-) create mode 100644 node/service/Cargo.toml create mode 100644 node/service/README.adoc create mode 100644 node/service/src/chain_spec.rs create mode 100644 node/service/src/client.rs create mode 100644 node/service/src/grandpa_support.rs create mode 100644 node/service/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 396df57e0b63..7377ebcccc6d 100644 --- a/Cargo.lock +++ b/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/Cargo.toml b/Cargo.toml index 7b6d6aed5502..2833abcceb7f 100644 --- a/Cargo.toml +++ b/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/cli/Cargo.toml b/cli/Cargo.toml index ec4df900be11..b631dd1e7807 100644 --- a/cli/Cargo.toml +++ b/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/cli/src/command.rs b/cli/src/command.rs index 9890d39544aa..5e9d78382833 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -15,7 +15,10 @@ // along with Polkadot. If not, see . 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/cli/src/lib.rs b/cli/src/lib.rs index 3e31fa0bb2ae..be2f3c6cd646 100644 --- a/cli/src/lib.rs +++ b/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/collator/Cargo.toml b/collator/Cargo.toml index a2e8691e7a64..52d04fba10fe 100644 --- a/collator/Cargo.toml +++ b/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/collator/src/lib.rs b/collator/src/lib.rs index 08706a7cb090..932648f3e159 100644 --- a/collator/src/lib.rs +++ b/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

( Ok(collation) } +#[cfg(feature = "service-rewr")] +fn build_collator_service( + _spawner: SP, + _handles: FullNodeHandles, + _client: Arc, + _para_id: ParaId, + _key: Arc, + _build_parachain_context: P, +) -> Result, polkadot_service::Error> + where + C: PolkadotClient< + service::Block, + service::TFullBackend, + R + > + 'static, + R: ConstructRuntimeApi + Sync + Send, + >::RuntimeApi: + sp_api::ApiExt< + service::Block, + StateBackend = as service::Backend>::State, + > + + RuntimeApiCollection< + Extrinsic, + StateBackend = as service::Backend>::State, + > + + Sync + Send, + P: BuildParachainContext, + P::ParachainContext: Send + 'static, + ::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( spawner: SP, - handles: polkadot_service::FullNodeHandles, + handles: FullNodeHandles, client: Arc, para_id: ParaId, key: Arc, @@ -408,6 +451,7 @@ where Ok(()) } +#[cfg(not(feature = "service-rewr"))] fn compute_targets(para_id: ParaId, session_keys: &[ValidatorId], roster: DutyRoster) -> HashSet { use polkadot_primitives::parachain::Chain; diff --git a/node/overseer/src/lib.rs b/node/overseer/src/lib.rs index abe648bfa194..56bf953f61de 100644 --- a/node/overseer/src/lib.rs +++ b/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>( - client: P, + client: Arc

, mut handler: OverseerHandler, ) -> SubsystemResult<()> { let mut finality = client.finality_notification_stream(); diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml new file mode 100644 index 000000000000..74069f0233af --- /dev/null +++ b/node/service/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "polkadot-service-new" +version = "0.8.3" +authors = ["Parity Technologies "] +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/node/service/README.adoc b/node/service/README.adoc new file mode 100644 index 000000000000..2196d5467806 --- /dev/null +++ b/node/service/README.adoc @@ -0,0 +1,5 @@ + += Polkadot Service + +placeholder +//TODO Write content :) (https://github.com/paritytech/polkadot/issues/159) diff --git a/node/service/src/chain_spec.rs b/node/service/src/chain_spec.rs new file mode 100644 index 000000000000..0659d0808301 --- /dev/null +++ b/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 . + +//! 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, + /// Known bad block hashes. + pub bad_blocks: sc_client_api::BadBlocks, +} + +/// 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::from_json_bytes(&include_bytes!("../../../service/res/polkadot.json")[..]) +} + +pub fn kusama_config() -> Result { + KusamaChainSpec::from_json_bytes(&include_bytes!("../../../service/res/kusama.json")[..]) +} + +pub fn westend_config() -> Result { + 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::>(), + }), + 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::>(), + }), + 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::>(), + }), + 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(seed: &str) -> ::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(seed: &str) -> AccountId where + AccountPublic: From<::Public> +{ + AccountPublic::from(get_from_seed::(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::(&format!("{}//stash", seed)), + get_account_id_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + ) +} + +fn testnet_accounts() -> Vec { + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("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>, +) -> polkadot::GenesisConfig { + let endowed_accounts: Vec = 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::>(), + }), + 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>, +) -> kusama::GenesisConfig { + let endowed_accounts: Vec = 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::>(), + }), + 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>, +) -> westend::GenesisConfig { + let endowed_accounts: Vec = 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::>(), + }), + 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::("Alice"), + None, + ) +} + +fn kusama_development_config_genesis() -> kusama::GenesisConfig { + kusama_testnet_genesis( + vec![ + get_authority_keys_from_seed("Alice"), + ], + get_account_id_from_seed::("Alice"), + None, + ) +} + +fn westend_development_config_genesis() -> westend::GenesisConfig { + westend_testnet_genesis( + vec![ + get_authority_keys_from_seed("Alice"), + ], + get_account_id_from_seed::("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::("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::("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::("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/node/service/src/client.rs b/node/service/src/client.rs new file mode 100644 index 000000000000..28d2bccabbe5 --- /dev/null +++ b/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 . + +//! 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: + BlockchainEvents + Sized + Send + Sync + + ProvideRuntimeApi + + HeaderBackend + + CallApiAt< + Block, + Error = sp_blockchain::Error, + StateBackend = Backend ::State + > + where + Block: BlockT, + Backend: BackendT, + Runtime: ConstructRuntimeApi +{} + +impl PolkadotClient for Client + where + Block: BlockT, + Runtime: ConstructRuntimeApi, + Backend: BackendT, + Client: BlockchainEvents + ProvideRuntimeApi + HeaderBackend + + Sized + Send + Sync + + CallApiAt< + Block, + Error = sp_blockchain::Error, + StateBackend = Backend ::State + > +{} diff --git a/node/service/src/grandpa_support.rs b/node/service/src/grandpa_support.rs new file mode 100644 index 000000000000..a875c4b45a37 --- /dev/null +++ b/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 . + +//! 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(pub(crate) N, pub(crate) N); + +impl grandpa::VotingRule for PauseAfterBlockFor> where + Block: BlockT, + B: sp_blockchain::HeaderBackend, +{ + fn restrict_vote( + &self, + backend: &B, + base: &Block::Header, + best_target: &Block::Header, + current_target: &Block::Header, + ) -> Option<(Block::Hash, NumberFor)> { + 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, + 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::>(); + + 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/node/service/src/lib.rs b/node/service/src/lib.rs new file mode 100644 index 000000000000..af517adc0e9e --- /dev/null +++ b/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 . + +//! 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: + sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::ApiExt + + babe_primitives::BabeApi + + grandpa_primitives::GrandpaApi + + ParachainHost + + sp_block_builder::BlockBuilder + + system_rpc_runtime_api::AccountNonceApi + + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + + sp_api::Metadata + + sp_offchain::OffchainWorkerApi + + sp_session::SessionKeys + + authority_discovery_primitives::AuthorityDiscoveryApi +where + Extrinsic: RuntimeExtrinsic, + >::StateBackend: sp_api::StateBackend, +{} + +impl RuntimeApiCollection for Api +where + Api: + sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::ApiExt + + babe_primitives::BabeApi + + grandpa_primitives::GrandpaApi + + ParachainHost + + sp_block_builder::BlockBuilder + + system_rpc_runtime_api::AccountNonceApi + + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + + sp_api::Metadata + + sp_offchain::OffchainWorkerApi + + sp_session::SessionKeys + + authority_discovery_primitives::AuthorityDiscoveryApi, + Extrinsic: RuntimeExtrinsic, + >::StateBackend: sp_api::StateBackend, +{} + +pub trait RuntimeExtrinsic: codec::Codec + Send + Sync + 'static {} + +impl 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 { + 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 for ValidationSubsystem { + fn start(&mut self, mut ctx: SubsystemContext) -> SpawnedSubsystem { + SpawnedSubsystem(Box::pin(async move { + while let Ok(_) = ctx.recv().await {} + })) + } +} + +struct CandidateBackingSubsystem; + +impl Subsystem for CandidateBackingSubsystem { + fn start(&mut self, mut ctx: SubsystemContext) -> SpawnedSubsystem { + SpawnedSubsystem(Box::pin(async move { + while let Ok(_) = ctx.recv().await {} + })) + } +} + +fn real_overseer( + leaves: impl IntoIterator, + s: S, +) -> Result<(Overseer, 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>; + 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::($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>; + 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(mut config: Configuration) + -> Result, ServiceError> +where + Runtime: ConstructRuntimeApi> + Send + Sync + 'static, + Runtime::RuntimeApi: + RuntimeApiCollection, Block>>, + Dispatch: NativeExecutionDispatch + 'static, + Extrinsic: RuntimeExtrinsic, + >::StateBackend: sp_api::StateBackend, +{ + 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, + _authority_discovery_enabled: bool, + _slot_duration: u64, + grandpa_pause: Option<(u32, u32)>, + informant_prefix: Option, +) + -> Result<( + impl AbstractService, + Arc, + 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, + _authority_discovery_enabled: bool, + _slot_duration: u64, + grandpa_pause: Option<(u32, u32)>, + informant_prefix: Option, +) -> Result<( + impl AbstractService, + Arc, + 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, + _authority_discovery_enabled: bool, + _slot_duration: u64, + grandpa_pause: Option<(u32, u32)>, + informant_prefix: Option, +) + -> Result<( + impl AbstractService, + Arc, + 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, + SelectChain = LongestChain, Block>, + CallExecutor = TLightCallExecutor, + >, 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, + SelectChain = LongestChain, Block>, + CallExecutor = TLightCallExecutor, + >, 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, + SelectChain = LongestChain, Block>, + CallExecutor = TLightCallExecutor + >, + ServiceError> +{ + new_light!(config, westend_runtime::RuntimeApi, KusamaExecutor) +}