From 16ff3579ef61f8f80bebf4a594d0a81cd524057e Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Sat, 14 Jul 2018 18:07:11 +0200 Subject: [PATCH] Split polkadot-service (#310) * Substrate service * Splitting polkadot service * Specialised components * Specialised components * Docs and style * Docs and style * Final touches * Added db key assertion --- polkadot/api/src/full.rs | 11 +- polkadot/api/src/light.rs | 9 +- polkadot/cli/Cargo.toml | 2 + polkadot/cli/src/chain_spec.rs | 8 +- polkadot/cli/src/informant.rs | 16 +- polkadot/cli/src/lib.rs | 17 +- polkadot/collator/src/lib.rs | 6 +- polkadot/network/src/lib.rs | 6 + polkadot/service/Cargo.toml | 11 +- polkadot/service/src/chain_spec.rs | 428 ++++++++++---------------- polkadot/service/src/components.rs | 258 ---------------- polkadot/service/src/config.rs | 71 ----- polkadot/service/src/error.rs | 32 -- polkadot/service/src/lib.rs | 435 +++++++++++++++------------ polkadot/src/main.rs | 6 +- polkadot/transaction-pool/src/lib.rs | 10 +- 16 files changed, 442 insertions(+), 884 deletions(-) delete mode 100644 polkadot/service/src/components.rs delete mode 100644 polkadot/service/src/config.rs delete mode 100644 polkadot/service/src/error.rs diff --git a/polkadot/api/src/full.rs b/polkadot/api/src/full.rs index 7c39bc3c6c..131a780bef 100644 --- a/polkadot/api/src/full.rs +++ b/polkadot/api/src/full.rs @@ -16,7 +16,7 @@ //! Strongly typed API for full Polkadot client. -use client::backend::{Backend, LocalBackend}; +use client::backend::LocalBackend; use client::block_builder::BlockBuilder as ClientBlockBuilder; use client::{Client, LocalCallExecutor}; use polkadot_executor::Executor as LocalDispatch; @@ -57,9 +57,7 @@ macro_rules! with_runtime { }} } -impl> BlockBuilder for ClientBlockBuilder>, Block> - where ::client::error::Error: From<<>::State as state_machine::backend::Backend>::Error> -{ +impl> BlockBuilder for ClientBlockBuilder>, Block> { fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { self.push(extrinsic).map_err(Into::into) } @@ -70,9 +68,7 @@ impl> BlockBuilder for ClientBlockBuilder> PolkadotApi for Client>, Block> - where ::client::error::Error: From<<>::State as state_machine::backend::Backend>::Error> -{ +impl> PolkadotApi for Client>, Block> { type BlockBuilder = ClientBlockBuilder>, Block>; fn session_keys(&self, at: &BlockId) -> Result> { @@ -160,7 +156,6 @@ impl> PolkadotApi for Client> LocalPolkadotApi for Client>, Block> - where ::client::error::Error: From<<>::State as state_machine::backend::Backend>::Error> {} #[cfg(test)] diff --git a/polkadot/api/src/light.rs b/polkadot/api/src/light.rs index e20c1a245e..c0eb691e32 100644 --- a/polkadot/api/src/light.rs +++ b/polkadot/api/src/light.rs @@ -20,7 +20,6 @@ use std::sync::Arc; use client::backend::{Backend, RemoteBackend}; use client::{Client, CallExecutor}; use codec::Slicable; -use state_machine; use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; use runtime::Address; use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; @@ -43,9 +42,7 @@ impl BlockBuilder for LightBlockBuilder { /// Remote polkadot API implementation. pub struct RemotePolkadotApiWrapper, E: CallExecutor>(pub Arc>); -impl, E: CallExecutor> PolkadotApi for RemotePolkadotApiWrapper - where ::client::error::Error: From<<>::State as state_machine::backend::Backend>::Error> -{ +impl, E: CallExecutor> PolkadotApi for RemotePolkadotApiWrapper { type BlockBuilder = LightBlockBuilder; fn session_keys(&self, at: &BlockId) -> Result> { @@ -104,6 +101,4 @@ impl, E: CallExecutor> PolkadotApi for RemotePolkadotAp } } -impl, E: CallExecutor> RemotePolkadotApi for RemotePolkadotApiWrapper - where ::client::error::Error: From<<>::State as state_machine::backend::Backend>::Error> -{} +impl, E: CallExecutor> RemotePolkadotApi for RemotePolkadotApiWrapper {} diff --git a/polkadot/cli/Cargo.toml b/polkadot/cli/Cargo.toml index 0d47f2eea7..b2bf91db90 100644 --- a/polkadot/cli/Cargo.toml +++ b/polkadot/cli/Cargo.toml @@ -27,11 +27,13 @@ serde = "1.0" exit-future = "0.1" substrate-client = { path = "../../substrate/client" } substrate-codec = { path = "../../substrate/codec" } +substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" } substrate-network = { path = "../../substrate/network" } substrate-primitives = { path = "../../substrate/primitives" } substrate-rpc = { path = "../../substrate/rpc" } substrate-rpc-servers = { path = "../../substrate/rpc-servers" } substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } +substrate-service = { path = "../../substrate/service" } substrate-state-machine = { path = "../../substrate/state-machine" } substrate-telemetry = { path = "../../substrate/telemetry" } polkadot-primitives = { path = "../primitives" } diff --git a/polkadot/cli/src/chain_spec.rs b/polkadot/cli/src/chain_spec.rs index e5dc6f280b..5b4ae48204 100644 --- a/polkadot/cli/src/chain_spec.rs +++ b/polkadot/cli/src/chain_spec.rs @@ -39,10 +39,10 @@ pub enum ChainSpec { impl ChainSpec { pub(crate) fn load(self) -> Result { Ok(match self { - ChainSpec::PoC1Testnet => service::ChainSpec::poc_1_testnet_config()?, - ChainSpec::Development => service::ChainSpec::development_config(), - ChainSpec::LocalTestnet => service::ChainSpec::local_testnet_config(), - ChainSpec::StagingTestnet => service::ChainSpec::staging_testnet_config(), + ChainSpec::PoC1Testnet => service::chain_spec::poc_1_testnet_config()?, + ChainSpec::Development => service::chain_spec::development_config(), + ChainSpec::LocalTestnet => service::chain_spec::local_testnet_config(), + ChainSpec::StagingTestnet => service::chain_spec::staging_testnet_config(), ChainSpec::Custom(f) => service::ChainSpec::from_json_file(PathBuf::from(f))?, }) } diff --git a/polkadot/cli/src/informant.rs b/polkadot/cli/src/informant.rs index 9146decf62..a624bce626 100644 --- a/polkadot/cli/src/informant.rs +++ b/polkadot/cli/src/informant.rs @@ -22,9 +22,9 @@ use service::{Service, Components}; use tokio::runtime::TaskExecutor; use tokio::timer::Interval; use network::{SyncState, SyncProvider}; -use polkadot_primitives::Block; -use state_machine; -use client::{self, BlockchainEvents}; +use client::BlockchainEvents; +use runtime_primitives::traits::{Header, As}; +use substrate_extrinsic_pool::api::ExtrinsicPool; const TIMER_INTERVAL_MS: u64 = 5000; @@ -32,13 +32,12 @@ const TIMER_INTERVAL_MS: u64 = 5000; pub fn start(service: &Service, exit: ::exit_future::Exit, handle: TaskExecutor) where C: Components, - client::error::Error: From<<<::Backend as client::backend::Backend>::State as state_machine::Backend>::Error>, { let interval = Interval::new(Instant::now(), Duration::from_millis(TIMER_INTERVAL_MS)); let network = service.network(); let client = service.client(); - let txpool = service.transaction_pool(); + let txpool = service.extrinsic_pool(); let display_notifications = interval.map_err(|e| debug!("Timer error: {:?}", e)).for_each(move |_| { let sync_status = network.status(); @@ -52,8 +51,9 @@ pub fn start(service: &Service, exit: ::exit_future::Exit, handle: TaskExe (SyncState::Downloading, Some(n)) => format!("Syncing, target=#{}", n), }; let txpool_status = txpool.light_status(); - info!(target: "polkadot", "{} ({} peers), best: #{} ({})", status, sync_status.num_peers, best_block.number, hash); - telemetry!("system.interval"; "status" => status, "peers" => num_peers, "height" => best_block.number, "best" => ?hash, "txcount" => txpool_status.transaction_count); + let best_number: u64 = best_block.number().as_(); + info!(target: "polkadot", "{} ({} peers), best: #{} ({})", status, sync_status.num_peers, best_number, hash); + telemetry!("system.interval"; "status" => status, "peers" => num_peers, "height" => best_number, "best" => ?hash, "txcount" => txpool_status.transaction_count); } else { warn!("Error getting best block information"); } @@ -66,7 +66,7 @@ pub fn start(service: &Service, exit: ::exit_future::Exit, handle: TaskExe Ok(()) }); - let txpool = service.transaction_pool(); + let txpool = service.extrinsic_pool(); let display_txpool_import = txpool.import_notification_stream().for_each(move |_| { let status = txpool.light_status(); telemetry!("txpool.import"; "mem_usage" => status.mem_usage, "count" => status.transaction_count, "sender" => status.senders); diff --git a/polkadot/cli/src/lib.rs b/polkadot/cli/src/lib.rs index d51ee7bed2..99fb5571b6 100644 --- a/polkadot/cli/src/lib.rs +++ b/polkadot/cli/src/lib.rs @@ -41,6 +41,8 @@ extern crate substrate_rpc; extern crate substrate_rpc_servers as rpc; extern crate substrate_runtime_primitives as runtime_primitives; extern crate substrate_state_machine as state_machine; +extern crate substrate_extrinsic_pool; +extern crate substrate_service; extern crate polkadot_primitives; extern crate polkadot_runtime; extern crate polkadot_service as service; @@ -76,7 +78,7 @@ use std::fs::File; use std::net::SocketAddr; use std::path::{Path, PathBuf}; use substrate_telemetry::{init_telemetry, TelemetryConfig}; -use polkadot_primitives::{Block, BlockId}; +use polkadot_primitives::BlockId; use codec::Slicable; use client::BlockOrigin; use runtime_primitives::generic::SignedBlock; @@ -141,8 +143,7 @@ pub trait Worker { fn exit_only(self) -> Self::Exit; /// Do work and schedule exit. - fn work(self, service: &Service) -> Self::Work - where ClientError: From<<<::Backend as ClientBackend>::State as StateMachineBackend>::Error>; + fn work(self, service: &Service) -> Self::Work; } /// Parse command line arguments and start the node. @@ -441,7 +442,6 @@ fn run_until_exit( where C: service::Components, W: Worker, - client::error::Error: From<<<::Backend as client::backend::Backend>::State as state_machine::Backend>::Error>, { let (exit_send, exit) = exit_future::signal(); @@ -453,10 +453,11 @@ fn run_until_exit( let ws_address = parse_address("127.0.0.1:9944", "ws-port", matches)?; let handler = || { - let chain = rpc::apis::chain::Chain::new(service.client(), executor.clone()); - let author = rpc::apis::author::Author::new(service.client(), service.transaction_pool()); - rpc::rpc_handler::( - service.client(), + let client = (&service as &substrate_service::Service).client(); + let chain = rpc::apis::chain::Chain::new(client.clone(), executor.clone()); + let author = rpc::apis::author::Author::new(client.clone(), service.extrinsic_pool()); + rpc::rpc_handler::, _, _, _, _>( + client, chain, author, sys_conf.clone(), diff --git a/polkadot/collator/src/lib.rs b/polkadot/collator/src/lib.rs index a4629c5793..7da24c51b6 100644 --- a/polkadot/collator/src/lib.rs +++ b/polkadot/collator/src/lib.rs @@ -66,7 +66,7 @@ use client::BlockchainEvents; use polkadot_api::PolkadotApi; use polkadot_primitives::BlockId; use polkadot_primitives::parachain::{self, BlockData, HeadData, ConsolidatedIngress, Collation, Message, Id as ParaId}; -use polkadot_cli::{ClientError, ServiceComponents, ClientBackend, PolkadotBlock, StateMachineBackend, Service}; +use polkadot_cli::{ServiceComponents, Service}; use polkadot_cli::Worker; /// Parachain context needed for collation. @@ -213,9 +213,7 @@ impl Worker for CollationNode where self.exit } - fn work(self, service: &Service) -> Self::Work - where ClientError: From<<<::Backend as ClientBackend>::State as StateMachineBackend>::Error>, - { + fn work(self, service: &Service) -> Self::Work { let CollationNode { parachain_context, exit, para_id, key } = self; let client = service.client(); let api = service.api(); diff --git a/polkadot/network/src/lib.rs b/polkadot/network/src/lib.rs index 1a9b995e5f..fc143e92af 100644 --- a/polkadot/network/src/lib.rs +++ b/polkadot/network/src/lib.rs @@ -221,6 +221,12 @@ pub struct PolkadotProtocol { next_req_id: u64, } +impl Default for PolkadotProtocol { + fn default() -> Self { + Self::new() + } +} + impl PolkadotProtocol { /// Instantiate a polkadot protocol handler. pub fn new() -> Self { diff --git a/polkadot/service/Cargo.toml b/polkadot/service/Cargo.toml index f17c50b0d9..b2f5f97c4d 100644 --- a/polkadot/service/Cargo.toml +++ b/polkadot/service/Cargo.toml @@ -4,18 +4,12 @@ version = "0.2.0" authors = ["Parity Technologies "] [dependencies] -futures = "0.1.17" parking_lot = "0.4" error-chain = "0.12" lazy_static = "1.0" log = "0.3" slog = "^2" -clap = "2.27" tokio = "0.1.7" -exit-future = "0.1" -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" hex-literal = "0.1" ed25519 = { path = "../../substrate/ed25519" } polkadot-primitives = { path = "../primitives" } @@ -27,12 +21,9 @@ polkadot-transaction-pool = { path = "../transaction-pool" } polkadot-network = { path = "../network" } substrate-keystore = { path = "../../substrate/keystore" } substrate-runtime-io = { path = "../../substrate/runtime-io" } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } substrate-primitives = { path = "../../substrate/primitives" } substrate-network = { path = "../../substrate/network" } substrate-client = { path = "../../substrate/client" } -substrate-client-db = { path = "../../substrate/client/db" } substrate-codec = { path = "../../substrate/codec" } -substrate-executor = { path = "../../substrate/executor" } -substrate-state-machine = { path = "../../substrate/state-machine" } +substrate-service = { path = "../../substrate/service" } substrate-telemetry = { path = "../../substrate/telemetry" } diff --git a/polkadot/service/src/chain_spec.rs b/polkadot/service/src/chain_spec.rs index ccab794b05..fec7916101 100644 --- a/polkadot/service/src/chain_spec.rs +++ b/polkadot/service/src/chain_spec.rs @@ -17,299 +17,173 @@ //! Polkadot chain configurations. use ed25519; -use std::collections::HashMap; -use std::fs::File; -use std::path::PathBuf; -use primitives::{AuthorityId, storage::{StorageKey, StorageData}}; -use runtime_primitives::{BuildStorage, StorageMap}; +use primitives::AuthorityId; use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, SessionConfig, StakingConfig, TimestampConfig}; -use serde_json as json; +use service::ChainSpec; -enum GenesisSource { - File(PathBuf), - Embedded(&'static [u8]), - Factory(fn() -> Genesis), +pub fn poc_1_testnet_config() -> Result, String> { + ChainSpec::from_embedded(include_bytes!("../res/poc-1.json")) } -impl GenesisSource { - fn resolve(&self) -> Result { - #[derive(Serialize, Deserialize)] - struct GenesisContainer { - genesis: Genesis, - } +fn staging_testnet_config_genesis() -> GenesisConfig { + let initial_authorities = vec![ + hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(), + hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(), + hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(), + hex!["8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c"].into(), + ]; + let endowed_accounts = vec![ + hex!["f295940fa750df68a686fcf4abd4111c8a9c5a5a5a83c4c8639c451a94a7adfd"].into(), + ]; + GenesisConfig { + consensus: Some(ConsensusConfig { + code: include_bytes!("../../runtime/wasm/genesis.wasm").to_vec(), // TODO change + authorities: initial_authorities.clone(), + }), + system: None, + session: Some(SessionConfig { + validators: initial_authorities.iter().cloned().map(Into::into).collect(), + session_length: 60, // that's 5 minutes per session. + broken_percent_late: 50, + }), + staking: Some(StakingConfig { + current_era: 0, + intentions: initial_authorities.iter().cloned().map(Into::into).collect(), + transaction_base_fee: 100, + transaction_byte_fee: 1, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, + early_era_slash: 10000, + session_reward: 100, + balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(), + validator_count: 12, + sessions_per_era: 12, // 1 hour per era + bonding_duration: 24, // 1 day per bond. + }), + democracy: Some(DemocracyConfig { + launch_period: 12 * 60 * 24, // 1 day per public referendum + voting_period: 12 * 60 * 24 * 3, // 3 days to discuss & vote on an active referendum + minimum_deposit: 5000, // 12000 as the minimum deposit for a referendum + }), + council: Some(CouncilConfig { + active_council: vec![], + candidacy_bond: 5000, // 5000 to become a council candidate + voter_bond: 1000, // 1000 down to vote for a candidate + present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation. + carry_count: 6, // carry over the 6 runners-up to the next council election + presentation_duration: 12 * 60 * 24, // one day for presenting winners. + approval_voting_period: 12 * 60 * 24 * 2, // two days period between possible council elections. + term_duration: 12 * 60 * 24 * 24, // 24 day term duration for the council. + desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit. + inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped. - match *self { - GenesisSource::File(ref path) => { - let file = File::open(path).map_err(|e| format!("Error opening spec file: {}", e))?; - let genesis: GenesisContainer = json::from_reader(file).map_err(|e| format!("Error parsing spec file: {}", e))?; - Ok(genesis.genesis) - }, - GenesisSource::Embedded(buf) => { - let genesis: GenesisContainer = json::from_reader(buf).map_err(|e| format!("Error parsing embedded file: {}", e))?; - Ok(genesis.genesis) - }, - GenesisSource::Factory(f) => Ok(f()), - } + cooloff_period: 12 * 60 * 24 * 4, // 4 day cooling off period if council member vetoes a proposal. + voting_period: 12 * 60 * 24, // 1 day voting period for council members. + }), + parachains: Some(Default::default()), + timestamp: Some(TimestampConfig { + period: 5, // 5 second block time. + }), } } -impl<'a> BuildStorage for &'a ChainSpec { - fn build_storage(self) -> Result { - match self.genesis.resolve()? { - Genesis::Runtime(gc) => gc.build_storage(), - Genesis::Raw(map) => Ok(map.into_iter().map(|(k, v)| (k.0, v.0)).collect()), - } +/// Staging testnet config. +pub fn staging_testnet_config() -> ChainSpec { + let boot_nodes = vec![ + "enode://a93a29fa68d965452bf0ff8c1910f5992fe2273a72a1ee8d3a3482f68512a61974211ba32bb33f051ceb1530b8ba3527fc36224ba6b9910329025e6d9153cf50@104.211.54.233:30333".into(), + "enode://051b18f63a316c4c5fef4631f8c550ae0adba179153588406fac3e5bbbbf534ebeda1bf475dceda27a531f6cdef3846ab6a010a269aa643a1fec7bff51af66bd@104.211.48.51:30333".into(), + "enode://c831ec9011d2c02d2c4620fc88db6d897a40d2f88fd75f47b9e4cf3b243999acb6f01b7b7343474650b34eeb1363041a422a91f1fc3850e43482983ee15aa582@104.211.48.247:30333".into(), + ]; + ChainSpec::from_genesis("Staging Testnet", staging_testnet_config_genesis, boot_nodes) +} + +fn testnet_genesis(initial_authorities: Vec) -> GenesisConfig { + let endowed_accounts = vec![ + ed25519::Pair::from_seed(b"Alice ").public().0.into(), + ed25519::Pair::from_seed(b"Bob ").public().0.into(), + ed25519::Pair::from_seed(b"Charlie ").public().0.into(), + ed25519::Pair::from_seed(b"Dave ").public().0.into(), + ed25519::Pair::from_seed(b"Eve ").public().0.into(), + ed25519::Pair::from_seed(b"Ferdie ").public().0.into(), + ]; + GenesisConfig { + consensus: Some(ConsensusConfig { + code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(), + authorities: initial_authorities.clone(), + }), + system: None, + session: Some(SessionConfig { + validators: initial_authorities.iter().cloned().map(Into::into).collect(), + session_length: 10, + broken_percent_late: 30, + }), + staking: Some(StakingConfig { + current_era: 0, + intentions: initial_authorities.iter().cloned().map(Into::into).collect(), + transaction_base_fee: 1, + transaction_byte_fee: 0, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, + balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(), + validator_count: 2, + sessions_per_era: 5, + bonding_duration: 2, + early_era_slash: 0, + session_reward: 0, + }), + democracy: Some(DemocracyConfig { + launch_period: 9, + voting_period: 18, + minimum_deposit: 10, + }), + council: Some(CouncilConfig { + active_council: endowed_accounts.iter().filter(|a| initial_authorities.iter().find(|&b| a.0 == b.0).is_none()).map(|a| (a.clone(), 1000000)).collect(), + candidacy_bond: 10, + voter_bond: 2, + present_slash_per_voter: 1, + carry_count: 4, + presentation_duration: 10, + approval_voting_period: 20, + term_duration: 1000000, + desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32, + inactive_grace_period: 1, + + cooloff_period: 75, + voting_period: 20, + }), + parachains: Some(Default::default()), + timestamp: Some(TimestampConfig { + period: 5, // 5 second block time. + }), } } -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -enum Genesis { - Runtime(GenesisConfig), - Raw(HashMap), +fn development_config_genesis() -> GenesisConfig { + testnet_genesis(vec![ + ed25519::Pair::from_seed(b"Alice ").public().into(), + ]) } -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct ChainSpecFile { - pub name: String, - pub boot_nodes: Vec, +/// Development config (single validator Alice) +pub fn development_config() -> ChainSpec { + ChainSpec::from_genesis("Development", development_config_genesis, vec![]) } -/// A configuration of a chain. Can be used to build a genesis block. -pub struct ChainSpec { - spec: ChainSpecFile, - genesis: GenesisSource, +fn local_testnet_genesis() -> GenesisConfig { + testnet_genesis(vec![ + ed25519::Pair::from_seed(b"Alice ").public().into(), + ed25519::Pair::from_seed(b"Bob ").public().into(), + ]) } -impl ChainSpec { - pub fn boot_nodes(&self) -> &[String] { - &self.spec.boot_nodes - } - - pub fn name(&self) -> &str { - &self.spec.name - } - - /// Parse json content into a `ChainSpec` - pub fn from_embedded(json: &'static [u8]) -> Result { - let spec = json::from_slice(json).map_err(|e| format!("Error parsing spec file: {}", e))?; - Ok(ChainSpec { - spec, - genesis: GenesisSource::Embedded(json), - }) - } - - /// Parse json file into a `ChainSpec` - pub fn from_json_file(path: PathBuf) -> Result { - let file = File::open(&path).map_err(|e| format!("Error opening spec file: {}", e))?; - let spec = json::from_reader(file).map_err(|e| format!("Error parsing spec file: {}", e))?; - Ok(ChainSpec { - spec, - genesis: GenesisSource::File(path), - }) - } - - /// Dump to json string. - pub fn to_json(self, raw: bool) -> Result { - #[derive(Serialize, Deserialize)] - struct Container { - #[serde(flatten)] - spec: ChainSpecFile, - #[serde(flatten)] - genesis: Genesis, - - }; - let genesis = match (raw, self.genesis.resolve()?) { - (true, Genesis::Runtime(g)) => { - let storage = g.build_storage()?.into_iter() - .map(|(k, v)| (StorageKey(k), StorageData(v))) - .collect(); - - Genesis::Raw(storage) - }, - (_, genesis) => genesis, - }; - let spec = Container { - spec: self.spec, - genesis, - }; - json::to_string_pretty(&spec).map_err(|e| format!("Error generating spec json: {}", e)) - } - - pub fn poc_1_testnet_config() -> Result { - Self::from_embedded(include_bytes!("../res/poc-1.json")) - } - - fn staging_testnet_config_genesis() -> Genesis { - let initial_authorities = vec![ - hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(), - hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(), - hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(), - hex!["8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c"].into(), - ]; - let endowed_accounts = vec![ - hex!["f295940fa750df68a686fcf4abd4111c8a9c5a5a5a83c4c8639c451a94a7adfd"].into(), - ]; - Genesis::Runtime(GenesisConfig { - consensus: Some(ConsensusConfig { - code: include_bytes!("../../runtime/wasm/genesis.wasm").to_vec(), // TODO change - authorities: initial_authorities.clone(), - }), - system: None, - session: Some(SessionConfig { - validators: initial_authorities.iter().cloned().map(Into::into).collect(), - session_length: 60, // that's 5 minutes per session. - broken_percent_late: 50, - }), - staking: Some(StakingConfig { - current_era: 0, - intentions: initial_authorities.iter().cloned().map(Into::into).collect(), - transaction_base_fee: 100, - transaction_byte_fee: 1, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, - contract_fee: 0, - reclaim_rebate: 0, - early_era_slash: 10000, - session_reward: 100, - balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(), - validator_count: 12, - sessions_per_era: 12, // 1 hour per era - bonding_duration: 24, // 1 day per bond. - }), - democracy: Some(DemocracyConfig { - launch_period: 12 * 60 * 24, // 1 day per public referendum - voting_period: 12 * 60 * 24 * 3, // 3 days to discuss & vote on an active referendum - minimum_deposit: 5000, // 12000 as the minimum deposit for a referendum - }), - council: Some(CouncilConfig { - active_council: vec![], - candidacy_bond: 5000, // 5000 to become a council candidate - voter_bond: 1000, // 1000 down to vote for a candidate - present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation. - carry_count: 6, // carry over the 6 runners-up to the next council election - presentation_duration: 12 * 60 * 24, // one day for presenting winners. - approval_voting_period: 12 * 60 * 24 * 2, // two days period between possible council elections. - term_duration: 12 * 60 * 24 * 24, // 24 day term duration for the council. - desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit. - inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped. - - cooloff_period: 12 * 60 * 24 * 4, // 4 day cooling off period if council member vetoes a proposal. - voting_period: 12 * 60 * 24, // 1 day voting period for council members. - }), - parachains: Some(Default::default()), - timestamp: Some(TimestampConfig { - period: 5, // 5 second block time. - }), - }) - } - /// Staging testnet config. - pub fn staging_testnet_config() -> Self { - let boot_nodes = vec![ - "enode://a93a29fa68d965452bf0ff8c1910f5992fe2273a72a1ee8d3a3482f68512a61974211ba32bb33f051ceb1530b8ba3527fc36224ba6b9910329025e6d9153cf50@104.211.54.233:30333".into(), - "enode://051b18f63a316c4c5fef4631f8c550ae0adba179153588406fac3e5bbbbf534ebeda1bf475dceda27a531f6cdef3846ab6a010a269aa643a1fec7bff51af66bd@104.211.48.51:30333".into(), - "enode://c831ec9011d2c02d2c4620fc88db6d897a40d2f88fd75f47b9e4cf3b243999acb6f01b7b7343474650b34eeb1363041a422a91f1fc3850e43482983ee15aa582@104.211.48.247:30333".into(), - ]; - ChainSpec { - spec: ChainSpecFile { name: "Staging Testnet".to_owned(), boot_nodes }, - genesis: GenesisSource::Factory(Self::staging_testnet_config_genesis), - } - } - - fn testnet_genesis(initial_authorities: Vec) -> Genesis { - let endowed_accounts = vec![ - ed25519::Pair::from_seed(b"Alice ").public().0.into(), - ed25519::Pair::from_seed(b"Bob ").public().0.into(), - ed25519::Pair::from_seed(b"Charlie ").public().0.into(), - ed25519::Pair::from_seed(b"Dave ").public().0.into(), - ed25519::Pair::from_seed(b"Eve ").public().0.into(), - ed25519::Pair::from_seed(b"Ferdie ").public().0.into(), - ]; - Genesis::Runtime(GenesisConfig { - consensus: Some(ConsensusConfig { - code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(), - authorities: initial_authorities.clone(), - }), - system: None, - session: Some(SessionConfig { - validators: initial_authorities.iter().cloned().map(Into::into).collect(), - session_length: 10, - broken_percent_late: 30, - }), - staking: Some(StakingConfig { - current_era: 0, - intentions: initial_authorities.iter().cloned().map(Into::into).collect(), - transaction_base_fee: 1, - transaction_byte_fee: 0, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, - contract_fee: 0, - reclaim_rebate: 0, - balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(), - validator_count: 2, - sessions_per_era: 5, - bonding_duration: 2, - early_era_slash: 0, - session_reward: 0, - }), - democracy: Some(DemocracyConfig { - launch_period: 9, - voting_period: 18, - minimum_deposit: 10, - }), - council: Some(CouncilConfig { - active_council: endowed_accounts.iter().filter(|a| initial_authorities.iter().find(|&b| a.0 == b.0).is_none()).map(|a| (a.clone(), 1000000)).collect(), - candidacy_bond: 10, - voter_bond: 2, - present_slash_per_voter: 1, - carry_count: 4, - presentation_duration: 10, - approval_voting_period: 20, - term_duration: 1000000, - desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32, - inactive_grace_period: 1, - - cooloff_period: 75, - voting_period: 20, - }), - parachains: Some(Default::default()), - timestamp: Some(TimestampConfig { - period: 5, // 5 second block time. - }), - }) - } - - fn development_config_genesis() -> Genesis { - Self::testnet_genesis(vec![ - ed25519::Pair::from_seed(b"Alice ").public().into(), - ]) - } - - /// Development config (single validator Alice) - pub fn development_config() -> Self { - ChainSpec { - spec: ChainSpecFile { name: "Development".to_owned(), boot_nodes: vec![] }, - genesis: GenesisSource::Factory(Self::development_config_genesis), - } - } - - fn local_testnet_genesis() -> Genesis { - Self::testnet_genesis(vec![ - ed25519::Pair::from_seed(b"Alice ").public().into(), - ed25519::Pair::from_seed(b"Bob ").public().into(), - ]) - } - - /// Local testnet config (multivalidator Alice + Bob) - pub fn local_testnet_config() -> Self { - ChainSpec { - spec: ChainSpecFile { name: "Local Testnet".to_owned(), boot_nodes: vec![] }, - genesis: GenesisSource::Factory(Self::local_testnet_genesis), - } - } +/// Local testnet config (multivalidator Alice + Bob) +pub fn local_testnet_config() -> ChainSpec { + ChainSpec::from_genesis("Local Testnet", local_testnet_genesis, vec![]) } diff --git a/polkadot/service/src/components.rs b/polkadot/service/src/components.rs deleted file mode 100644 index 44e16e5ff4..0000000000 --- a/polkadot/service/src/components.rs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2017 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 components. - -use std::collections::HashMap; -use std::sync::Arc; -use chain_spec::ChainSpec; -use client_db; -use client::{self, Client}; -use codec::{self, Slicable}; -use consensus; -use error; -use keystore::Store as Keystore; -use network::{self, OnDemand}; -use polkadot_api; -use polkadot_executor::Executor as LocalDispatch; -use polkadot_network::NetworkService; -use polkadot_primitives::{Block, BlockId, Hash}; -use state_machine::{self, ExecutionStrategy}; -use substrate_executor::NativeExecutor; -use transaction_pool::{self, TransactionPool}; -use tokio::runtime::TaskExecutor; - -/// Code executor. -pub type CodeExecutor = NativeExecutor; - -/// Polkadot service components. -pub trait Components { - /// Client backend type. - type Backend: 'static + client::backend::Backend; - - /// Polkadot API type. - type Api: 'static + polkadot_api::PolkadotApi + Send + Sync; - - /// Code executor type. - type Executor: 'static + client::CallExecutor + Send + Sync; - - /// Create client. - fn build_client(&self, settings: client_db::DatabaseSettings, executor: CodeExecutor, chain_spec: &ChainSpec, execution_strategy: ExecutionStrategy) - -> Result<(Arc>, Option>>), error::Error>; - - /// Create api. - fn build_api(&self, client: Arc>) -> Arc; - - /// Create network transaction pool adapter. - fn build_network_tx_pool(&self, client: Arc>, tx_pool: Arc>) - -> Arc>; - - /// Create consensus service. - fn build_consensus( - &self, - client: Arc>, - network: Arc, - tx_pool: Arc>, - keystore: &Keystore, - task_executor: TaskExecutor, - ) - -> Result, error::Error>; -} - -/// Components for full Polkadot service. -pub struct FullComponents { - /// Is this a validator node? - pub is_validator: bool, -} - -impl Components for FullComponents { - type Backend = client_db::Backend; - type Api = Client; - type Executor = client::LocalCallExecutor, NativeExecutor>; - - fn build_client(&self, db_settings: client_db::DatabaseSettings, executor: CodeExecutor, chain_spec: &ChainSpec, execution_strategy: ExecutionStrategy) - -> Result<(Arc>, Option>>), error::Error> { - Ok((Arc::new(client_db::new_client(db_settings, executor, chain_spec, execution_strategy)?), None)) - } - - fn build_api(&self, client: Arc>) -> Arc { - client - } - - fn build_network_tx_pool(&self, client: Arc>, pool: Arc>) - -> Arc> { - Arc::new(TransactionPoolAdapter { - imports_external_transactions: true, - pool, - client, - }) - } - - fn build_consensus( - &self, - client: Arc>, - network: Arc, - tx_pool: Arc>, - keystore: &Keystore, - task_executor: TaskExecutor, - ) - -> Result, error::Error> - { - if !self.is_validator { - return Ok(None); - } - - // Load the first available key - let key = keystore.load(&keystore.contents()?[0], "")?; - info!("Using authority key {}", key.public()); - - let consensus_net = ::polkadot_network::consensus::ConsensusNetwork::new(network, client.clone()); - Ok(Some(consensus::Service::new( - client.clone(), - client.clone(), - consensus_net, - tx_pool.clone(), - task_executor, - ::std::time::Duration::from_millis(4000), // TODO: dynamic - key, - ))) - } -} - -/// Components for light Polkadot service. -pub struct LightComponents; - -impl Components for LightComponents { - type Backend = client::light::backend::Backend, network::OnDemand>; - type Api = polkadot_api::light::RemotePolkadotApiWrapper; - type Executor = client::light::call_executor::RemoteCallExecutor< - client::light::blockchain::Blockchain, network::OnDemand>, - network::OnDemand>; - - fn build_client(&self, db_settings: client_db::DatabaseSettings, executor: CodeExecutor, spec: &ChainSpec, _execution_strategy: ExecutionStrategy) - -> Result<(Arc>, Option>>), error::Error> { - let db_storage = client_db::light::LightStorage::new(db_settings)?; - let light_blockchain = client::light::new_light_blockchain(db_storage); - let fetch_checker = Arc::new(client::light::new_fetch_checker(light_blockchain.clone(), executor)); - let fetcher = Arc::new(network::OnDemand::new(fetch_checker)); - let client_backend = client::light::new_light_backend(light_blockchain, fetcher.clone()); - let client = client::light::new_light(client_backend, fetcher.clone(), spec)?; - Ok((Arc::new(client), Some(fetcher))) - } - - fn build_api(&self, client: Arc>) -> Arc { - Arc::new(polkadot_api::light::RemotePolkadotApiWrapper(client.clone())) - } - - fn build_network_tx_pool(&self, client: Arc>, pool: Arc>) - -> Arc> { - Arc::new(TransactionPoolAdapter { - imports_external_transactions: false, - pool, - client, - }) - } - - fn build_consensus( - &self, - _client: Arc>, - _network: Arc, - _tx_pool: Arc>, - _keystore: &Keystore, - _task_executor: TaskExecutor, - ) - -> Result, error::Error> - { - Ok(None) - } -} - -/// Transaction pool adapter. -pub struct TransactionPoolAdapter where A: Send + Sync, E: Send + Sync { - imports_external_transactions: bool, - pool: Arc>, - client: Arc>, -} - -impl TransactionPoolAdapter - where - A: Send + Sync, - B: client::backend::Backend + Send + Sync, - E: client::CallExecutor + Send + Sync, - client::error::Error: From<<>::State as state_machine::backend::Backend>::Error>, -{ - fn best_block_id(&self) -> Option { - self.client.info() - .map(|info| BlockId::hash(info.chain.best_hash)) - .map_err(|e| { - debug!("Error getting best block: {:?}", e); - }) - .ok() - } -} - -impl network::TransactionPool for TransactionPoolAdapter - where - B: client::backend::Backend + Send + Sync, - E: client::CallExecutor + Send + Sync, - client::error::Error: From<<>::State as state_machine::backend::Backend>::Error>, - A: polkadot_api::PolkadotApi + Send + Sync, -{ - fn transactions(&self) -> Vec<(Hash, Vec)> { - let best_block_id = match self.best_block_id() { - Some(id) => id, - None => return vec![], - }; - self.pool.cull_and_get_pending(best_block_id, |pending| pending - .map(|t| { - let hash = t.hash().clone(); - (hash, t.primitive_extrinsic()) - }) - .collect() - ).unwrap_or_else(|e| { - warn!("Error retrieving pending set: {}", e); - vec![] - }) - } - - fn import(&self, transaction: &Vec) -> Option { - if !self.imports_external_transactions { - return None; - } - - let encoded = transaction.encode(); - if let Some(uxt) = codec::Slicable::decode(&mut &encoded[..]) { - let best_block_id = self.best_block_id()?; - match self.pool.import_unchecked_extrinsic(best_block_id, uxt) { - Ok(xt) => Some(*xt.hash()), - Err(e) => match *e.kind() { - transaction_pool::ErrorKind::AlreadyImported(hash) => Some(hash[..].into()), - _ => { - debug!("Error adding transaction to the pool: {:?}", e); - None - }, - } - } - } else { - debug!("Error decoding transaction"); - None - } - } - - fn on_broadcasted(&self, propagations: HashMap>) { - self.pool.on_broadcasted(propagations) - } -} diff --git a/polkadot/service/src/config.rs b/polkadot/service/src/config.rs deleted file mode 100644 index c9fdc9ced1..0000000000 --- a/polkadot/service/src/config.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2017 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 .? - -//! Service configuration. - -use transaction_pool; -use chain_spec::ChainSpec; -pub use state_machine::ExecutionStrategy; -pub use network::Role; -pub use network::NetworkConfiguration; -pub use client_db::PruningMode; - -/// Service configuration. -pub struct Configuration { - /// Node roles. - pub roles: Role, - /// Transaction pool configuration. - pub transaction_pool: transaction_pool::Options, - /// Network configuration. - pub network: NetworkConfiguration, - /// Path to key files. - pub keystore_path: String, - /// Path to the database. - pub database_path: String, - /// Pruning settings. - pub pruning: PruningMode, - /// Additional key seeds. - pub keys: Vec, - /// Chain configuration. - pub chain_spec: ChainSpec, - /// Telemetry server URL, optional - only `Some` if telemetry reporting is enabled - pub telemetry: Option, - /// Node name. - pub name: String, - /// Execution strategy. - pub execution_strategy: ExecutionStrategy, -} - -impl Configuration { - /// Create default config for given chain spec. - pub fn default_with_spec(chain_spec: ChainSpec) -> Configuration { - let mut configuration = Configuration { - chain_spec, - name: Default::default(), - roles: Role::FULL, - transaction_pool: Default::default(), - network: Default::default(), - keystore_path: Default::default(), - database_path: Default::default(), - keys: Default::default(), - telemetry: Default::default(), - pruning: PruningMode::ArchiveAll, - execution_strategy: ExecutionStrategy::Both, - }; - configuration.network.boot_nodes = configuration.chain_spec.boot_nodes().to_vec(); - configuration - } -} diff --git a/polkadot/service/src/error.rs b/polkadot/service/src/error.rs deleted file mode 100644 index fbb6981407..0000000000 --- a/polkadot/service/src/error.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2017 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 . - -//! Errors that can occur during the service operation. - -use client; -use network; -use keystore; - -error_chain! { - links { - Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; - Network(network::error::Error, network::error::ErrorKind) #[doc="Network error"]; - Keystore(keystore::Error, keystore::ErrorKind) #[doc="Keystore error"]; - } - - errors { - } -} diff --git a/polkadot/service/src/lib.rs b/polkadot/service/src/lib.rs index 787e304c06..246afd096e 100644 --- a/polkadot/service/src/lib.rs +++ b/polkadot/service/src/lib.rs @@ -14,15 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Polkadot service. Starts a thread that spins the network, the client and the transaction pool. -//! Manages communication between them. +#![warn(unused_extern_crates)] + +//! Polkadot service. Specialized wrapper over substrate service. -extern crate futures; extern crate ed25519; -extern crate clap; -extern crate exit_future; -extern crate serde; -extern crate serde_json; extern crate polkadot_primitives; extern crate polkadot_runtime; extern crate polkadot_executor; @@ -32,236 +28,291 @@ extern crate polkadot_transaction_pool as transaction_pool; extern crate polkadot_network; extern crate substrate_keystore as keystore; extern crate substrate_primitives as primitives; -extern crate substrate_runtime_primitives as runtime_primitives; extern crate substrate_network as network; extern crate substrate_codec as codec; -extern crate substrate_executor; -extern crate substrate_state_machine as state_machine; extern crate substrate_client as client; -extern crate substrate_client_db as client_db; +extern crate substrate_service as service; extern crate tokio; -#[macro_use] -extern crate substrate_telemetry; -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` #[macro_use] extern crate log; #[macro_use] -extern crate serde_derive; -#[macro_use] extern crate hex_literal; -mod components; -mod error; -mod config; -mod chain_spec; +pub mod chain_spec; use std::sync::Arc; -use futures::prelude::*; +use std::collections::HashMap; + +use codec::Slicable; use transaction_pool::TransactionPool; use keystore::Store as Keystore; -use polkadot_api::PolkadotApi; +use polkadot_api::{PolkadotApi, light::RemotePolkadotApiWrapper}; use polkadot_primitives::{Block, BlockId, Hash}; -use client::{Client, BlockchainEvents}; -use network::ManageNetwork; -use exit_future::Signal; -use polkadot_network::{NetworkService, PolkadotProtocol}; +use polkadot_runtime::GenesisConfig; +use client::Client; +use polkadot_network::{PolkadotProtocol, consensus::ConsensusNetwork}; use tokio::runtime::TaskExecutor; -pub use self::error::{ErrorKind, Error}; -pub use self::components::{Components, FullComponents, LightComponents}; -pub use config::{Configuration, Role, PruningMode, ExecutionStrategy}; -pub use chain_spec::ChainSpec; +pub use service::{Configuration, Role, PruningMode, ExtrinsicPoolOptions, + ErrorKind, Error, ComponentBlock, LightComponents, FullComponents}; +pub use client::ExecutionStrategy; -/// Polkadot service. -pub struct Service { - client: Arc>, - api: Arc, - network: Arc, - transaction_pool: Arc>, - signal: Option, - _consensus: Option, +/// Specialised polkadot `ChainSpec`. +pub type ChainSpec = service::ChainSpec; +/// Polkadot client type for specialised `Components`. +pub type ComponentClient = Client<::Backend, ::Executor, Block>; + +/// A collection of type to generalise Polkadot specific components over full / light client. +pub trait Components: service::Components { + /// Polkadot API. + type Api: 'static + PolkadotApi + Send + Sync; + /// Client backend. + type Backend: 'static + client::backend::Backend; + /// Client executor. + type Executor: 'static + client::CallExecutor + Send + Sync; } -/// Creates light client and register protocol with the network service -pub fn new_light(config: Configuration, executor: TaskExecutor) -> Result, error::Error> { - Service::new(components::LightComponents, config, executor) +impl Components for service::LightComponents { + type Api = RemotePolkadotApiWrapper< + as service::Components>::Backend, + as service::Components>::Executor, + >; + type Executor = service::LightExecutor; + type Backend = service::LightBackend; } -/// Creates full client and register protocol with the network service -pub fn new_full(config: Configuration, executor: TaskExecutor) -> Result, error::Error> { - let is_validator = (config.roles & Role::AUTHORITY) == Role::AUTHORITY; - Service::new(components::FullComponents { is_validator }, config, executor) +impl Components for service::FullComponents { + type Api = service::FullClient; + type Executor = service::FullExecutor; + type Backend = service::FullBackend; } -/// Creates bare client without any networking. -pub fn new_client(config: Configuration) -> Result::Backend, - ::Executor, - Block>>, - error::Error> -{ - let db_settings = client_db::DatabaseSettings { - cache_size: None, - path: config.database_path.into(), - pruning: config.pruning, - }; - let executor = polkadot_executor::Executor::new(); - let is_validator = (config.roles & Role::AUTHORITY) == Role::AUTHORITY; - let components = components::FullComponents { is_validator }; - let (client, _) = components.build_client(db_settings, executor, &config.chain_spec, config.execution_strategy)?; - Ok(client) -} +/// Polkadot config for the substrate service. +pub struct Factory; -impl Service - where - Components: components::Components, - client::error::Error: From<<<::Backend as client::backend::Backend>::State as state_machine::Backend>::Error>, -{ - /// Creates and register protocol with the network service - fn new(components: Components, config: Configuration, task_executor: TaskExecutor) -> Result { - let (signal, exit) = ::exit_future::signal(); +impl service::ServiceFactory for Factory { + type Block = Block; + type NetworkProtocol = PolkadotProtocol; + type RuntimeDispatch = polkadot_executor::Executor; + type FullExtrinsicPool = TransactionPoolAdapter< + service::FullBackend, + service::FullExecutor, + service::FullClient + >; + type LightExtrinsicPool = TransactionPoolAdapter< + service::LightBackend, + service::LightExecutor, + RemotePolkadotApiWrapper, service::LightExecutor> + >; + type Genesis = GenesisConfig; - // Create client - let executor = polkadot_executor::Executor::with_heap_pages(128); + const NETWORK_PROTOCOL_ID: network::ProtocolId = ::polkadot_network::DOT_PROTOCOL_ID; - let mut keystore = Keystore::open(config.keystore_path.into())?; - for seed in &config.keys { - keystore.generate_from_seed(seed)?; - } - - if keystore.contents()?.is_empty() { - let key = keystore.generate("")?; - info!("Generated a new keypair: {:?}", key.public()); - } - - let db_settings = client_db::DatabaseSettings { - cache_size: None, - path: config.database_path.into(), - pruning: config.pruning, - }; - - let (client, on_demand) = components.build_client(db_settings, executor, &config.chain_spec, config.execution_strategy)?; - let api = components.build_api(client.clone()); - let best_header = client.best_block_header()?; - - info!("Best block: #{}", best_header.number); - telemetry!("node.start"; "height" => best_header.number, "best" => ?best_header.hash()); - - let transaction_pool = Arc::new(TransactionPool::new(config.transaction_pool, api.clone())); - let transaction_pool_adapter = components.build_network_tx_pool(client.clone(), transaction_pool.clone()); - let network_params = network::Params { - config: network::ProtocolConfig { - roles: config.roles, - }, - network_config: config.network, - chain: client.clone(), - on_demand: on_demand.clone().map(|d| d as Arc>), - transaction_pool: transaction_pool_adapter, - specialization: PolkadotProtocol::new(), - }; - - let network = network::Service::new(network_params, ::polkadot_network::DOT_PROTOCOL_ID)?; - on_demand.map(|on_demand| on_demand.set_service_link(Arc::downgrade(&network))); - - network.start_network(); - - { - // block notifications - let network = network.clone(); - let txpool = transaction_pool.clone(); - - let events = client.import_notification_stream() - .for_each(move |notification| { - network.on_block_imported(notification.hash, ¬ification.header); - prune_imported(&*txpool, notification.hash); - Ok(()) - }) - .select(exit.clone()) - .then(|_| Ok(())); - task_executor.spawn(events); - } - - { - // transaction notifications - let network = network.clone(); - let events = transaction_pool.import_notification_stream() - // TODO [ToDr] Consider throttling? - .for_each(move |_| { - network.trigger_repropagate(); - Ok(()) - }) - .select(exit.clone()) - .then(|_| Ok(())); - - task_executor.spawn(events); - } - - // Spin consensus service if configured - let consensus_service = components.build_consensus( - client.clone(), - network.clone(), - transaction_pool.clone(), - &keystore, - task_executor, - )?; - - Ok(Service { + fn build_full_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result + { + let api = client.clone(); + Ok(TransactionPoolAdapter { + pool: Arc::new(TransactionPool::new(config, api)), client: client, - network: network, - transaction_pool: transaction_pool, - signal: Some(signal), - api: api, - _consensus: consensus_service, + imports_external_transactions: true, }) } - /// Get shared client instance. - pub fn client(&self) -> Arc> { + fn build_light_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result + { + let api = Arc::new(RemotePolkadotApiWrapper(client.clone())); + Ok(TransactionPoolAdapter { + pool: Arc::new(TransactionPool::new(config, api)), + client: client, + imports_external_transactions: false, + }) + } +} + +/// Polkadot service. +pub struct Service { + inner: service::Service, + client: Arc>, + api: Arc<::Api>, + _consensus: Option, +} + +impl Service { + pub fn client(&self) -> Arc> { self.client.clone() } - /// Get shared polkadot-api instance. usually the same as the client. - pub fn api(&self) -> Arc { + pub fn api(&self) -> Arc<::Api> { self.api.clone() } - - /// Get shared network instance. - pub fn network(&self) -> Arc { - self.network.clone() - } - - /// Get shared transaction pool instance. - pub fn transaction_pool(&self) -> Arc> { - self.transaction_pool.clone() - } } -/// Produce a task which prunes any finalized transactions from the pool. -pub fn prune_imported(pool: &TransactionPool, hash: Hash) - where A: PolkadotApi, +/// Creates light client and register protocol with the network service +pub fn new_light(config: Configuration, executor: TaskExecutor) + -> Result>, Error> { - let block = BlockId::hash(hash); - if let Err(e) = pool.cull(block) { - warn!("Culling error: {:?}", e); - } + let service = service::Service::>::new(config, executor)?; + let api = Arc::new(RemotePolkadotApiWrapper(service.client())); + Ok(Service { + client: service.client(), + api: api, + inner: service, + _consensus: None, + }) +} - if let Err(e) = pool.retry_verification(block) { - warn!("Re-verifying error: {:?}", e); +/// Creates full client and register protocol with the network service +pub fn new_full(config: Configuration, executor: TaskExecutor) + -> Result>, Error> +{ + let keystore_path = config.keystore_path.clone(); + let is_validator = (config.roles & Role::AUTHORITY) == Role::AUTHORITY; + let service = service::Service::>::new(config, executor.clone())?; + let consensus = if is_validator { + // Spin consensus service if configured + let keystore = Keystore::open(keystore_path.into())?; + // Load the first available key + let key = keystore.load(&keystore.contents()?[0], "")?; + info!("Using authority key {}", key.public()); + + let client = service.client(); + + let consensus_net = ConsensusNetwork::new(service.network(), client.clone()); + Some(consensus::Service::new( + client.clone(), + client.clone(), + consensus_net, + service.extrinsic_pool(), + executor, + ::std::time::Duration::from_millis(4000), // TODO: dynamic + key, + )) + } else { + None + }; + + Ok(Service { + client: service.client(), + api: service.client(), + inner: service, + _consensus: consensus, + }) +} + +/// Creates bare client without any networking. +pub fn new_client(config: Configuration) +-> Result>>, Error> +{ + service::new_client::(config) +} + +impl ::std::ops::Deref for Service { + type Target = service::Service; + + fn deref(&self) -> &Self::Target { + &self.inner } } -impl Drop for Service where Components: components::Components { - fn drop(&mut self) { - debug!(target: "service", "Polkadot service shutdown"); +/// Transaction pool adapter. +pub struct TransactionPoolAdapter where A: Send + Sync, E: Send + Sync { + imports_external_transactions: bool, + pool: Arc>, + client: Arc>, +} - self.network.stop_network(); +impl TransactionPoolAdapter + where + A: Send + Sync, + B: client::backend::Backend + Send + Sync, + E: client::CallExecutor + Send + Sync, +{ + fn best_block_id(&self) -> Option { + self.client.info() + .map(|info| BlockId::hash(info.chain.best_hash)) + .map_err(|e| { + debug!("Error getting best block: {:?}", e); + }) + .ok() + } +} - if let Some(signal) = self.signal.take() { - signal.fire(); +impl network::TransactionPool for TransactionPoolAdapter + where + B: client::backend::Backend + Send + Sync, + E: client::CallExecutor + Send + Sync, + A: polkadot_api::PolkadotApi + Send + Sync, +{ + fn transactions(&self) -> Vec<(Hash, Vec)> { + let best_block_id = match self.best_block_id() { + Some(id) => id, + None => return vec![], + }; + self.pool.cull_and_get_pending(best_block_id, |pending| pending + .map(|t| { + let hash = t.hash().clone(); + (hash, t.primitive_extrinsic()) + }) + .collect() + ).unwrap_or_else(|e| { + warn!("Error retrieving pending set: {}", e); + vec![] + }) + } + + fn import(&self, transaction: &Vec) -> Option { + if !self.imports_external_transactions { + return None; + } + + let encoded = transaction.encode(); + if let Some(uxt) = codec::Slicable::decode(&mut &encoded[..]) { + let best_block_id = self.best_block_id()?; + match self.pool.import_unchecked_extrinsic(best_block_id, uxt) { + Ok(xt) => Some(*xt.hash()), + Err(e) => match *e.kind() { + transaction_pool::ErrorKind::AlreadyImported(hash) => Some(hash[..].into()), + _ => { + debug!("Error adding transaction to the pool: {:?}", e); + None + }, + } + } + } else { + debug!("Error decoding transaction"); + None } } + + fn on_broadcasted(&self, propagations: HashMap>) { + self.pool.on_broadcasted(propagations) + } } + +impl service::ExtrinsicPool for TransactionPoolAdapter + where + B: client::backend::Backend + Send + Sync + 'static, + E: client::CallExecutor + Send + Sync + 'static, + A: polkadot_api::PolkadotApi + Send + Sync + 'static, +{ + type Api = TransactionPool; + + fn prune_imported(&self, hash: &Hash) { + let block = BlockId::hash(*hash); + if let Err(e) = self.pool.cull(block) { + warn!("Culling error: {:?}", e); + } + + if let Err(e) = self.pool.retry_verification(block) { + warn!("Re-verifying error: {:?}", e); + } + } + + fn api(&self) -> Arc { + self.pool.clone() + } +} + diff --git a/polkadot/src/main.rs b/polkadot/src/main.rs index 65c87d85f0..8780126fcd 100644 --- a/polkadot/src/main.rs +++ b/polkadot/src/main.rs @@ -25,7 +25,7 @@ extern crate futures; #[macro_use] extern crate error_chain; -use cli::{ClientError, ServiceComponents, ClientBackend, PolkadotBlock, StateMachineBackend, Service}; +use cli::{ServiceComponents, Service}; use futures::sync::oneshot; use futures::{future, Future}; @@ -51,9 +51,7 @@ impl cli::Worker for Worker { exit.map_err(drop) } - fn work(self, _service: &Service) -> Self::Exit - where ClientError: From<<<::Backend as ClientBackend>::State as StateMachineBackend>::Error>, - { + fn work(self, _service: &Service) -> Self::Exit { self.exit_only() } } diff --git a/polkadot/transaction-pool/src/lib.rs b/polkadot/transaction-pool/src/lib.rs index a553afb94c..ee0b506f6f 100644 --- a/polkadot/transaction-pool/src/lib.rs +++ b/polkadot/transaction-pool/src/lib.rs @@ -45,7 +45,7 @@ use std::{ use codec::Slicable; use extrinsic_pool::{Pool, Listener, txpool::{self, Readiness, scoring::{Change, Choice}}}; -use extrinsic_pool::api::ExtrinsicPool; +use extrinsic_pool::api::{ExtrinsicPool, EventStream}; use polkadot_api::PolkadotApi; use primitives::{AccountId, BlockId, Hash, Index, UncheckedExtrinsic as FutureProofUncheckedExtrinsic}; use runtime::{Address, UncheckedExtrinsic}; @@ -403,6 +403,14 @@ impl ExtrinsicPool for Transact }) .collect() } + + fn light_status(&self) -> LightStatus { + self.inner.light_status() + } + + fn import_notification_stream(&self) -> EventStream { + self.inner.import_notification_stream() + } } #[cfg(test)]