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>
This commit is contained in:
Fedor Sakharov
2020-06-11 22:26:15 +03:00
committed by GitHub
parent 04c8603042
commit d7e17fc612
14 changed files with 2325 additions and 9 deletions
+62
View File
@@ -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"
+7
View File
@@ -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",
]
+7 -4
View File
@@ -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" ]
+4 -1
View File
@@ -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(())
}
+8
View File
@@ -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::*;
+7 -1
View File
@@ -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" ]
+46 -2
View File
@@ -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;
+2 -1
View File
@@ -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();
+69
View File
@@ -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 = []
+5
View File
@@ -0,0 +1,5 @@
= Polkadot Service
placeholder
//TODO Write content :) (https://github.com/paritytech/polkadot/issues/159)
+963
View File
@@ -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(),
)
}
+53
View File
@@ -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
>
{}
@@ -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,
);
}
}
+729
View File
@@ -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)
}