diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 1798a33151..d27ca6c9d6 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1419,11 +1419,13 @@ dependencies = [ "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-client 0.1.0", "substrate-codec 0.1.0", + "substrate-extrinsic-pool 0.1.0", "substrate-network 0.1.0", "substrate-primitives 0.1.0", "substrate-rpc 0.1.0", "substrate-rpc-servers 0.1.0", "substrate-runtime-primitives 0.1.0", + "substrate-service 0.2.0", "substrate-state-machine 0.1.0", "substrate-telemetry 0.2.0", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1562,11 +1564,8 @@ dependencies = [ name = "polkadot-service" version = "0.2.0" dependencies = [ - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "ed25519 0.1.0", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1578,20 +1577,14 @@ dependencies = [ "polkadot-primitives 0.1.0", "polkadot-runtime 0.1.0", "polkadot-transaction-pool 0.1.0", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-client 0.1.0", - "substrate-client-db 0.1.0", "substrate-codec 0.1.0", - "substrate-executor 0.1.0", "substrate-keystore 0.1.0", "substrate-network 0.1.0", "substrate-primitives 0.1.0", "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-state-machine 0.1.0", + "substrate-service 0.2.0", "substrate-telemetry 0.2.0", "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2294,7 +2287,6 @@ dependencies = [ "substrate-runtime-primitives 0.1.0", "substrate-runtime-support 0.1.0", "substrate-serializer 0.1.0", - "substrate-state-machine 0.1.0", "substrate-test-client 0.1.0", ] @@ -2609,6 +2601,33 @@ dependencies = [ "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-service" +version = "0.2.0" +dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-client 0.1.0", + "substrate-client-db 0.1.0", + "substrate-executor 0.1.0", + "substrate-extrinsic-pool 0.1.0", + "substrate-keystore 0.1.0", + "substrate-network 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-telemetry 0.2.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "substrate-state-db" version = "0.1.0" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 95abadcc08..48540d2551 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -57,6 +57,7 @@ members = [ "substrate/runtime/timestamp", "substrate/runtime/version", "substrate/serializer", + "substrate/service", "substrate/state-db", "substrate/state-machine", "substrate/test-runtime", diff --git a/substrate/demo/cli/src/lib.rs b/substrate/demo/cli/src/lib.rs index ac0146c860..75e3c207b6 100644 --- a/substrate/demo/cli/src/lib.rs +++ b/substrate/demo/cli/src/lib.rs @@ -64,6 +64,14 @@ impl extrinsic_pool::api::ExtrinsicPool for D { Err("unimplemented".into()) } + + fn light_status(&self) -> extrinsic_pool::txpool::LightStatus { + unreachable!() + } + + fn import_notification_stream(&self) -> extrinsic_pool::api::EventStream { + unreachable!() + } } struct DummySystem; diff --git a/substrate/polkadot/api/src/full.rs b/substrate/polkadot/api/src/full.rs index 7c39bc3c6c..131a780bef 100644 --- a/substrate/polkadot/api/src/full.rs +++ b/substrate/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/substrate/polkadot/api/src/light.rs b/substrate/polkadot/api/src/light.rs index e20c1a245e..c0eb691e32 100644 --- a/substrate/polkadot/api/src/light.rs +++ b/substrate/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/substrate/polkadot/cli/Cargo.toml b/substrate/polkadot/cli/Cargo.toml index 0d47f2eea7..b2bf91db90 100644 --- a/substrate/polkadot/cli/Cargo.toml +++ b/substrate/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/substrate/polkadot/cli/src/chain_spec.rs b/substrate/polkadot/cli/src/chain_spec.rs index e5dc6f280b..5b4ae48204 100644 --- a/substrate/polkadot/cli/src/chain_spec.rs +++ b/substrate/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/substrate/polkadot/cli/src/informant.rs b/substrate/polkadot/cli/src/informant.rs index 9146decf62..a624bce626 100644 --- a/substrate/polkadot/cli/src/informant.rs +++ b/substrate/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/substrate/polkadot/cli/src/lib.rs b/substrate/polkadot/cli/src/lib.rs index d51ee7bed2..99fb5571b6 100644 --- a/substrate/polkadot/cli/src/lib.rs +++ b/substrate/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/substrate/polkadot/collator/src/lib.rs b/substrate/polkadot/collator/src/lib.rs index a4629c5793..7da24c51b6 100644 --- a/substrate/polkadot/collator/src/lib.rs +++ b/substrate/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/substrate/polkadot/network/src/lib.rs b/substrate/polkadot/network/src/lib.rs index 1a9b995e5f..fc143e92af 100644 --- a/substrate/polkadot/network/src/lib.rs +++ b/substrate/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/substrate/polkadot/service/Cargo.toml b/substrate/polkadot/service/Cargo.toml index f17c50b0d9..b2f5f97c4d 100644 --- a/substrate/polkadot/service/Cargo.toml +++ b/substrate/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/substrate/polkadot/service/src/chain_spec.rs b/substrate/polkadot/service/src/chain_spec.rs index ccab794b05..fec7916101 100644 --- a/substrate/polkadot/service/src/chain_spec.rs +++ b/substrate/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/substrate/polkadot/service/src/components.rs b/substrate/polkadot/service/src/components.rs deleted file mode 100644 index 44e16e5ff4..0000000000 --- a/substrate/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/substrate/polkadot/service/src/lib.rs b/substrate/polkadot/service/src/lib.rs index 787e304c06..246afd096e 100644 --- a/substrate/polkadot/service/src/lib.rs +++ b/substrate/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/substrate/polkadot/src/main.rs b/substrate/polkadot/src/main.rs index 65c87d85f0..8780126fcd 100644 --- a/substrate/polkadot/src/main.rs +++ b/substrate/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/substrate/polkadot/transaction-pool/src/lib.rs b/substrate/polkadot/transaction-pool/src/lib.rs index a553afb94c..ee0b506f6f 100644 --- a/substrate/polkadot/transaction-pool/src/lib.rs +++ b/substrate/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)] diff --git a/substrate/substrate/client/db/src/lib.rs b/substrate/substrate/client/db/src/lib.rs index b1e5f98fa8..1e043dfd63 100644 --- a/substrate/substrate/client/db/src/lib.rs +++ b/substrate/substrate/client/db/src/lib.rs @@ -83,8 +83,6 @@ pub fn new_client( ) -> Result, client::LocalCallExecutor, E>, Block>, client::error::Error> where Block: BlockT, - ::Number: As, - Block::Hash: Into<[u8; 32]>, // TODO: remove when patricia_trie generic. E: CodeExecutor + RuntimeInfo, S: BuildStorage, { @@ -127,7 +125,7 @@ pub struct BlockchainDb { meta: RwLock::Number, Block::Hash>>, } -impl BlockchainDb where ::Number: As { +impl BlockchainDb { fn new(db: Arc) -> Result { let meta = read_meta::(&*db, columns::HEADER)?; Ok(BlockchainDb { @@ -148,7 +146,7 @@ impl BlockchainDb where ::Number } } -impl client::blockchain::HeaderBackend for BlockchainDb where ::Number: As { +impl client::blockchain::HeaderBackend for BlockchainDb { fn header(&self, id: BlockId) -> Result, client::error::Error> { match read_db(&*self.db, columns::BLOCK_INDEX, columns::HEADER, id)? { Some(header) => match Block::Header::decode(&mut &header[..]) { @@ -186,7 +184,7 @@ impl client::blockchain::HeaderBackend for BlockchainDb client::blockchain::Backend for BlockchainDb where ::Number: As { +impl client::blockchain::Backend for BlockchainDb { fn body(&self, id: BlockId) -> Result>, client::error::Error> { match read_db(&*self.db, columns::BLOCK_INDEX, columns::BODY, id)? { Some(body) => match Slicable::decode(&mut &body[..]) { @@ -276,7 +274,7 @@ pub struct Backend { finalization_window: u64, } -impl Backend where ::Number: As { +impl Backend { /// Create a new instance of database backend. pub fn new(config: DatabaseSettings, finalization_window: u64) -> Result { let db = open_database(&config, "full")?; @@ -325,10 +323,7 @@ fn apply_state_commit(transaction: &mut DBTransaction, commit: state_db::CommitS } } -impl client::backend::Backend for Backend where - ::Number: As, - Block::Hash: Into<[u8; 32]>, // TODO: remove when patricia_trie generic. -{ +impl client::backend::Backend for Backend { type BlockImportOperation = BlockImportOperation; type Blockchain = BlockchainDb; type State = DbState; @@ -377,7 +372,7 @@ impl client::backend::Backend for Backend where let finalizing_hash = if self.finalization_window == 0 { Some(hash) } else { - self.blockchain.hash(As::sa((number_u64 - self.finalization_window) as u32))? + self.blockchain.hash(As::sa((number_u64 - self.finalization_window) as u64))? }; if let Some(finalizing_hash) = finalizing_hash { trace!("Finalizing block #{} ({:?})", number_u64 - self.finalization_window, finalizing_hash); @@ -408,15 +403,13 @@ impl client::backend::Backend for Backend where } self.blockchain.header(block).and_then(|maybe_hdr| maybe_hdr.map(|hdr| { - let root: [u8; 32] = hdr.state_root().clone().into(); - DbState::with_storage(self.storage.clone(), root.into()) + let root: TrieH256 = TrieH256::from_slice(hdr.state_root().as_ref()); + DbState::with_storage(self.storage.clone(), root) }).ok_or_else(|| client::error::ErrorKind::UnknownBlock(format!("{:?}", block)).into())) } } -impl client::backend::LocalBackend for Backend where - ::Number: As, - Block::Hash: Into<[u8; 32]>, // TODO: remove when patricia_trie generic. +impl client::backend::LocalBackend for Backend {} #[cfg(test)] diff --git a/substrate/substrate/client/db/src/light.rs b/substrate/substrate/client/db/src/light.rs index deeec22320..8888026ed2 100644 --- a/substrate/substrate/client/db/src/light.rs +++ b/substrate/substrate/client/db/src/light.rs @@ -28,7 +28,7 @@ use client::light::blockchain::Storage as LightBlockchainStorage; use codec::Slicable; use primitives::AuthorityId; use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, Hash, HashFor, Zero}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashFor, Zero}; use utils::{meta_keys, Meta, db_err, number_to_db_key, open_database, read_db, read_id, read_meta}; use DatabaseSettings; @@ -55,7 +55,6 @@ struct BestAuthorities { impl LightStorage where Block: BlockT, - <::Header as HeaderT>::Number: As, { /// Create new storage with given settings. pub fn new(config: DatabaseSettings) -> ClientResult { @@ -98,7 +97,6 @@ impl LightStorage impl BlockchainHeaderBackend for LightStorage where Block: BlockT, - <::Header as HeaderT>::Number: As, { fn header(&self, id: BlockId) -> ClientResult> { match read_db(&*self.db, columns::BLOCK_INDEX, columns::HEADER, id)? { @@ -140,8 +138,6 @@ impl BlockchainHeaderBackend for LightStorage impl LightBlockchainStorage for LightStorage where Block: BlockT, - <::Header as HeaderT>::Number: As, - ::Hash: From<[u8; 32]> + Into<[u8; 32]>, { fn import_header(&self, is_new_best: bool, header: Block::Header) -> ClientResult<()> { let mut transaction = DBTransaction::new(); diff --git a/substrate/substrate/client/db/src/utils.rs b/substrate/substrate/client/db/src/utils.rs index 17250455fa..defe523d95 100644 --- a/substrate/substrate/client/db/src/utils.rs +++ b/substrate/substrate/client/db/src/utils.rs @@ -57,8 +57,9 @@ pub struct Meta { pub type BlockKey = [u8; 4]; /// Convert block number into key (LE representation). -pub fn number_to_db_key(n: N) -> BlockKey where N: As { - let n: u32 = n.as_(); +pub fn number_to_db_key(n: N) -> BlockKey where N: As { + let n: u64 = n.as_(); + assert!(n & 0xffffffff00000000 == 0); [ (n >> 24) as u8, @@ -108,7 +109,6 @@ pub fn open_database(config: &DatabaseSettings, db_type: &str) -> client::error: pub fn read_id(db: &KeyValueDB, col_index: Option, id: BlockId) -> Result, client::error::Error> where Block: BlockT, - <::Header as HeaderT>::Number: As, { match id { BlockId::Hash(h) => db.get(col_index, h.as_ref()) @@ -125,7 +125,6 @@ pub fn read_id(db: &KeyValueDB, col_index: Option, id: BlockId(db: &KeyValueDB, col_index: Option, col: Option, id: BlockId) -> client::error::Result> where Block: BlockT, - <::Header as HeaderT>::Number: As, { read_id(db, col_index, id).and_then(|key| match key { Some(key) => db.get(col, &key).map_err(db_err), @@ -137,7 +136,6 @@ pub fn read_db(db: &KeyValueDB, col_index: Option, col: Option, pub fn read_meta(db: &KeyValueDB, col_header: Option) -> Result::Header as HeaderT>::Number, Block::Hash>, client::error::Error> where Block: BlockT, - <::Header as HeaderT>::Number: As, { let genesis_number = <::Header as HeaderT>::Number::zero(); let (best_hash, best_number) = if let Some(Some(header)) = db.get(COLUMN_META, meta_keys::BEST_BLOCK).and_then(|id| diff --git a/substrate/substrate/client/src/block_builder.rs b/substrate/substrate/client/src/block_builder.rs index 326cd08d77..c5695ce4e4 100644 --- a/substrate/substrate/client/src/block_builder.rs +++ b/substrate/substrate/client/src/block_builder.rs @@ -28,7 +28,6 @@ pub struct BlockBuilder where B: backend::Backend, E: CallExecutor + Clone, Block: BlockT, - error::Error: From<<>::State as state_machine::backend::Backend>::Error>, { header: ::Header, extrinsics: Vec<::Extrinsic>, @@ -41,7 +40,6 @@ impl BlockBuilder where B: backend::Backend, E: CallExecutor + Clone, Block: BlockT, - error::Error: From<<>::State as state_machine::backend::Backend>::Error>, { /// Create a new instance of builder from the given client, building on the latest block. pub fn new(client: &Client) -> error::Result { diff --git a/substrate/substrate/client/src/call_executor.rs b/substrate/substrate/client/src/call_executor.rs index 61f067447f..ac561b5a54 100644 --- a/substrate/substrate/client/src/call_executor.rs +++ b/substrate/substrate/client/src/call_executor.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::Block as BlockT; -use state_machine::{self, OverlayedChanges, Ext, Backend as StateBackend, +use state_machine::{self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager, native_when_possible}; use runtime_io::Externalities; use executor::{RuntimeVersion, RuntimeInfo}; @@ -109,7 +109,6 @@ impl CallExecutor for LocalCallExecutor B: backend::LocalBackend, E: CodeExecutor + RuntimeInfo, Block: BlockT, - error::Error: From<<>::State as StateBackend>::Error>, { type Error = E::Error; diff --git a/substrate/substrate/client/src/client.rs b/substrate/substrate/client/src/client.rs index 4262c357f8..baa0681412 100644 --- a/substrate/substrate/client/src/client.rs +++ b/substrate/substrate/client/src/client.rs @@ -25,7 +25,7 @@ use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, use runtime_primitives::BuildStorage; use primitives::storage::{StorageKey, StorageData}; use codec::Slicable; -use state_machine::{self, Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor, ExecutionStrategy, ExecutionManager}; +use state_machine::{Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor, ExecutionStrategy, ExecutionManager}; use backend::{self, BlockImportOperation}; use blockchain::{self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend}; @@ -161,7 +161,6 @@ impl Client where B: backend::Backend, E: CallExecutor, Block: BlockT, - error::Error: From<<>::State as StateBackend>::Error>, { /// Creates new Substrate Client with given blockchain and code executor. pub fn new( @@ -202,7 +201,7 @@ impl Client where /// Return single storage entry of contract under given address in state in a block of given hash. pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result { Ok(StorageData(self.state_at(id)? - .storage(&key.0)? + .storage(&key.0).map_err(|e| error::Error::from_state(Box::new(e)))? .ok_or_else(|| error::ErrorKind::NoValueForKey(key.0.clone()))? .to_vec())) } @@ -461,7 +460,6 @@ impl bft::BlockImport for Client B: backend::Backend, E: CallExecutor, Block: BlockT, - error::Error: From<::Error> { fn import_block(&self, block: Block, justification: ::bft::Justification) { let (header, extrinsics) = block.deconstruct(); @@ -479,7 +477,6 @@ impl bft::Authorities for Client B: backend::Backend, E: CallExecutor, Block: BlockT, - error::Error: From<::Error>, { fn authorities(&self, at: &BlockId) -> Result, bft::Error> { let version: Result<_, bft::Error> = self.runtime_version_at(at).map_err(|_| bft::ErrorKind::InvalidRuntime.into()); @@ -499,7 +496,6 @@ impl BlockchainEvents for Client B: backend::Backend, E: CallExecutor, Block: BlockT, - error::Error: From<::Error> { /// Get block import event stream. fn import_notification_stream(&self) -> mpsc::UnboundedReceiver> { @@ -514,7 +510,6 @@ impl ChainHead for Client B: backend::Backend, E: CallExecutor, Block: BlockT, - error::Error: From<::Error> { fn best_block_header(&self) -> error::Result<::Header> { Client::best_block_header(self) diff --git a/substrate/substrate/client/src/error.rs b/substrate/substrate/client/src/error.rs index b9447d38a3..0b3e627ee6 100644 --- a/substrate/substrate/client/src/error.rs +++ b/substrate/substrate/client/src/error.rs @@ -132,4 +132,9 @@ impl Error { pub fn from_blockchain(e: Box) -> Self { ErrorKind::Blockchain(e).into() } + + /// Chain a state error. + pub fn from_state(e: Box) -> Self { + ErrorKind::Execution(e).into() + } } diff --git a/substrate/substrate/client/src/lib.rs b/substrate/substrate/client/src/lib.rs index 307be85ff5..329d5a5ac0 100644 --- a/substrate/substrate/client/src/lib.rs +++ b/substrate/substrate/client/src/lib.rs @@ -59,3 +59,4 @@ pub use client::{ }; pub use blockchain::Info as ChainInfo; pub use call_executor::{CallResult, CallExecutor, LocalCallExecutor}; +pub use state_machine::ExecutionStrategy; diff --git a/substrate/substrate/client/src/light/call_executor.rs b/substrate/substrate/client/src/light/call_executor.rs index 4498997241..06598a2936 100644 --- a/substrate/substrate/client/src/light/call_executor.rs +++ b/substrate/substrate/client/src/light/call_executor.rs @@ -22,7 +22,8 @@ use futures::{IntoFuture, Future}; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; -use state_machine::{Backend as StateBackend, CodeExecutor, OverlayedChanges, execution_proof_check, ExecutionManager}; +use state_machine::{Backend as StateBackend, CodeExecutor, OverlayedChanges, + execution_proof_check, TrieH256, ExecutionManager}; use blockchain::Backend as ChainBackend; use call_executor::{CallExecutor, CallResult}; @@ -104,7 +105,6 @@ pub fn check_execution_proof( ) -> ClientResult where Block: BlockT, - ::Hash: Into<[u8; 32]>, // TODO: remove when patricia_trie generic. B: ChainBackend, E: CodeExecutor, { @@ -116,18 +116,18 @@ pub fn check_execution_proof( /// Check remote execution proof using given state root. fn do_check_execution_proof( - local_state_root: [u8; 32], + local_state_root: Hash, executor: &E, request: &RemoteCallRequest, remote_proof: Vec>, ) -> ClientResult where - Hash: ::std::fmt::Display, + Hash: ::std::fmt::Display + ::std::convert::AsRef<[u8]>, E: CodeExecutor, { let mut changes = OverlayedChanges::default(); let (local_result, _) = execution_proof_check( - local_state_root, + TrieH256::from_slice(local_state_root.as_ref()).into(), remote_proof, &mut changes, executor, diff --git a/substrate/substrate/client/src/light/fetcher.rs b/substrate/substrate/client/src/light/fetcher.rs index 3527203b3f..90622bf6fc 100644 --- a/substrate/substrate/client/src/light/fetcher.rs +++ b/substrate/substrate/client/src/light/fetcher.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use futures::IntoFuture; -use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT}; +use runtime_primitives::traits::{Block as BlockT}; use state_machine::CodeExecutor; use call_executor::CallResult; @@ -78,8 +78,6 @@ impl LightDataChecker { impl FetchChecker for LightDataChecker where Block: BlockT, - ::Hash: From<[u8; 32]> + Into<[u8; 32]>, // TODO: remove when patricia_trie generic. - <::Header as HeaderT>::Number: As, S: BlockchainStorage, E: CodeExecutor, F: Fetcher, diff --git a/substrate/substrate/executor/src/native_executor.rs b/substrate/substrate/executor/src/native_executor.rs index 7eef0e95cd..de7e31a9f0 100644 --- a/substrate/substrate/executor/src/native_executor.rs +++ b/substrate/substrate/executor/src/native_executor.rs @@ -93,7 +93,7 @@ pub fn with_native_environment(ext: &mut Externalities, f: F) -> Result } /// Delegate for dispatching a CodeExecutor call to native code. -pub trait NativeExecutionDispatch { +pub trait NativeExecutionDispatch: Send + Sync { /// Get the wasm code that the native dispatch will be equivalent to. fn native_equivalent() -> &'static [u8]; @@ -108,14 +108,14 @@ pub trait NativeExecutionDispatch { /// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence /// and dispatch to native code when possible, falling back on `WasmExecutor` when not. #[derive(Debug)] -pub struct NativeExecutor { +pub struct NativeExecutor { /// Dummy field to avoid the compiler complaining about us not using `D`. _dummy: ::std::marker::PhantomData, /// The fallback executor in case native isn't available. fallback: WasmExecutor, } -impl NativeExecutor { +impl NativeExecutor { /// Create new instance with 128 pages for the wasm fallback's heap. pub fn new() -> Self { Self::with_heap_pages(128) @@ -135,7 +135,13 @@ impl NativeExecutor { } } -impl Clone for NativeExecutor { +impl Default for NativeExecutor { + fn default() -> Self { + Self::new() + } +} + +impl Clone for NativeExecutor { fn clone(&self) -> Self { NativeExecutor { _dummy: Default::default(), @@ -144,7 +150,7 @@ impl Clone for NativeExecutor { } } -impl RuntimeInfo for NativeExecutor { +impl RuntimeInfo for NativeExecutor { const NATIVE_VERSION: Option = Some(D::VERSION); fn runtime_version( @@ -160,7 +166,7 @@ impl RuntimeInfo for NativeExecutor } } -impl CodeExecutor for NativeExecutor { +impl CodeExecutor for NativeExecutor { type Error = Error; fn call( diff --git a/substrate/substrate/extrinsic-pool/src/api.rs b/substrate/substrate/extrinsic-pool/src/api.rs index 31ec0f5346..b7bb3afac9 100644 --- a/substrate/substrate/extrinsic-pool/src/api.rs +++ b/substrate/substrate/extrinsic-pool/src/api.rs @@ -17,6 +17,7 @@ //! External API for extrinsic pool. use txpool; +use futures::sync::mpsc; /// Extrinsic pool error. pub trait Error: ::std::error::Error + Send + Sized { @@ -32,6 +33,9 @@ impl Error for txpool::Error { fn into_pool_error(self) -> Result { Ok(self) } } +/// Modification notification event stream type; +pub type EventStream = mpsc::UnboundedReceiver<()>; + /// Extrinsic pool. pub trait ExtrinsicPool: Send + Sync + 'static { /// Error type @@ -39,4 +43,10 @@ pub trait ExtrinsicPool: Send + Sync + 'static { /// Submit a collection of extrinsics to the pool. fn submit(&self, block: BlockId, xt: Vec) -> Result, Self::Error>; + + /// Returns light status of the pool. + fn light_status(&self) -> txpool::LightStatus; + + /// Return an event stream of transactions imported to the pool. + fn import_notification_stream(&self) -> EventStream; } diff --git a/substrate/substrate/extrinsic-pool/src/pool.rs b/substrate/substrate/extrinsic-pool/src/pool.rs index ca8df7c6ac..488834398e 100644 --- a/substrate/substrate/extrinsic-pool/src/pool.rs +++ b/substrate/substrate/extrinsic-pool/src/pool.rs @@ -18,7 +18,7 @@ use std::{ collections::HashMap, fmt, marker::PhantomData, - sync::{Arc, Weak}, + sync::Arc, }; use futures::sync::mpsc; @@ -40,7 +40,7 @@ pub struct Pool where S, Listener, >>, - import_notification_sinks: Mutex>>>, + import_notification_sinks: Mutex>>, } impl Pool where @@ -62,15 +62,14 @@ impl Pool where pub fn import(&self, xt: VEx) -> Result, E> { let result = self.pool.write().import(xt)?; - let weak = Arc::downgrade(&result); self.import_notification_sinks.lock() - .retain(|sink| sink.unbounded_send(weak.clone()).is_ok()); + .retain(|sink| sink.unbounded_send(()).is_ok()); Ok(result) } /// Return an event stream of transactions imported to the pool. - pub fn import_notification_stream(&self) -> mpsc::UnboundedReceiver> { + pub fn import_notification_stream(&self) -> mpsc::UnboundedReceiver<()> { let (sink, stream) = mpsc::unbounded(); self.import_notification_sinks.lock().push(sink); stream diff --git a/substrate/substrate/network/Cargo.toml b/substrate/substrate/network/Cargo.toml index bd132992f8..93066e8d38 100644 --- a/substrate/substrate/network/Cargo.toml +++ b/substrate/substrate/network/Cargo.toml @@ -24,7 +24,6 @@ ethcore-io = { git = "https://github.com/paritytech/parity.git" } ed25519 = { path = "../../substrate/ed25519" } substrate-primitives = { path = "../../substrate/primitives" } substrate-client = { path = "../../substrate/client" } -substrate-state-machine = { path = "../../substrate/state-machine" } substrate-serializer = { path = "../../substrate/serializer" } substrate-runtime-support = { path = "../../substrate/runtime-support" } substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } diff --git a/substrate/substrate/network/src/blocks.rs b/substrate/substrate/network/src/blocks.rs index f5b32ad387..d0f7c75744 100644 --- a/substrate/substrate/network/src/blocks.rs +++ b/substrate/substrate/network/src/blocks.rs @@ -20,7 +20,7 @@ use std::ops::Range; use std::collections::{HashMap, BTreeMap}; use std::collections::hash_map::Entry; use network::PeerId; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; +use runtime_primitives::traits::{Block as BlockT, NumberFor, As}; use message; const MAX_PARALLEL_DOWNLOADS: u32 = 1; @@ -35,17 +35,17 @@ pub struct BlockData { #[derive(Debug)] enum BlockRangeState { Downloading { - len: u64, + len: NumberFor, downloading: u32, }, Complete(Vec>), } -impl BlockRangeState where B::Header: HeaderT { - pub fn len(&self) -> u64 { +impl BlockRangeState { + pub fn len(&self) -> NumberFor { match *self { BlockRangeState::Downloading { len, .. } => len, - BlockRangeState::Complete(ref blocks) => blocks.len() as u64, + BlockRangeState::Complete(ref blocks) => As::sa(blocks.len() as u64), } } } @@ -54,11 +54,11 @@ impl BlockRangeState where B::Header: HeaderT { #[derive(Default)] pub struct BlockCollection { /// Downloaded blocks. - blocks: BTreeMap>, - peer_requests: HashMap, + blocks: BTreeMap, BlockRangeState>, + peer_requests: HashMap>, } -impl BlockCollection where B::Header: HeaderT { +impl BlockCollection { /// Create a new instance. pub fn new() -> Self { BlockCollection { @@ -74,7 +74,7 @@ impl BlockCollection where B::Header: HeaderT { } /// Insert a set of blocks into collection. - pub fn insert(&mut self, start: u64, blocks: Vec>, peer_id: PeerId) { + pub fn insert(&mut self, start: NumberFor, blocks: Vec>, peer_id: PeerId) { if blocks.is_empty() { return; } @@ -96,22 +96,22 @@ impl BlockCollection where B::Header: HeaderT { } /// Returns a set of block hashes that require a header download. The returned set is marked as being downloaded. - pub fn needed_blocks(&mut self, peer_id: PeerId, count: usize, peer_best: u64, common: u64) -> Option> { + pub fn needed_blocks(&mut self, peer_id: PeerId, count: usize, peer_best: NumberFor, common: NumberFor) -> Option>> { // First block number that we need to download - let first_different = common + 1; - let count = count as u64; + let first_different = common + As::sa(1); + let count = As::sa(count as u64); let (mut range, downloading) = { let mut downloading_iter = self.blocks.iter().peekable(); - let mut prev: Option<(&u64, &BlockRangeState)> = None; + let mut prev: Option<(&NumberFor, &BlockRangeState)> = None; loop { let next = downloading_iter.next(); break match &(prev, next) { &(Some((start, &BlockRangeState::Downloading { ref len, downloading })), _) if downloading < MAX_PARALLEL_DOWNLOADS => (*start .. *start + *len, downloading), - &(Some((start, r)), Some((next_start, _))) if start + r.len() < *next_start => + &(Some((start, r)), Some((next_start, _))) if *start + r.len() < *next_start => (*start + r.len() .. cmp::min(*next_start, *start + r.len() + count), 0), // gap &(Some((start, r)), None) => - (start + r.len() .. start + r.len() + count, 0), // last range + (*start + r.len() .. *start + r.len() + count, 0), // last range &(None, None) => (first_different .. first_different + count, 0), // empty &(None, Some((start, _))) if *start > first_different => @@ -128,7 +128,7 @@ impl BlockCollection where B::Header: HeaderT { trace!(target: "sync", "Out of range for peer {} ({} vs {})", peer_id, range.start, peer_best); return None; } - range.end = cmp::min(peer_best + 1, range.end); + range.end = cmp::min(peer_best + As::sa(1), range.end); self.peer_requests.insert(peer_id, range.start); self.blocks.insert(range.start, BlockRangeState::Downloading{ len: range.end - range.start, downloading: downloading + 1 }); if range.end <= range.start { @@ -138,7 +138,7 @@ impl BlockCollection where B::Header: HeaderT { } /// Get a valid chain of blocks ordered in descending order and ready for importing into blockchain. - pub fn drain(&mut self, from: u64) -> Vec> { + pub fn drain(&mut self, from: NumberFor) -> Vec> { let mut drained = Vec::new(); let mut ranges = Vec::new(); { @@ -146,7 +146,7 @@ impl BlockCollection where B::Header: HeaderT { for (start, range_data) in &mut self.blocks { match range_data { &mut BlockRangeState::Complete(ref mut blocks) if *start <= prev => { - prev = *start + blocks.len() as u64; + prev = *start + As::sa(blocks.len() as u64); let mut blocks = mem::replace(blocks, Vec::new()); drained.append(&mut blocks); ranges.push(*start); diff --git a/substrate/substrate/network/src/chain.rs b/substrate/substrate/network/src/chain.rs index bff88b8717..2c2e1cfaed 100644 --- a/substrate/substrate/network/src/chain.rs +++ b/substrate/substrate/network/src/chain.rs @@ -18,7 +18,6 @@ use client::{self, Client as SubstrateClient, ImportResult, ClientInfo, BlockStatus, BlockOrigin, CallExecutor}; use client::error::Error; -use state_machine; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; use runtime_primitives::generic::BlockId; use runtime_primitives::bft::Justification; @@ -54,8 +53,7 @@ impl Client for SubstrateClient where B: client::backend::Backend + Send + Sync + 'static, E: CallExecutor + Send + Sync + 'static, Block: BlockT, - Error: From<<>::State as state_machine::backend::Backend>::Error>, { - +{ fn import(&self, is_best: bool, header: Block::Header, justification: Justification, body: Option>) -> Result { // TODO: defer justification check. let justified_header = self.check_justification(header, justification.into())?; diff --git a/substrate/substrate/network/src/lib.rs b/substrate/substrate/network/src/lib.rs index f38b614bdc..bfa949a527 100644 --- a/substrate/substrate/network/src/lib.rs +++ b/substrate/substrate/network/src/lib.rs @@ -26,7 +26,6 @@ extern crate linked_hash_map; extern crate rand; extern crate parking_lot; extern crate substrate_primitives as primitives; -extern crate substrate_state_machine as state_machine; extern crate substrate_serializer as ser; extern crate substrate_client as client; extern crate substrate_runtime_support as runtime_support; diff --git a/substrate/substrate/network/src/on_demand.rs b/substrate/substrate/network/src/on_demand.rs index a96ca987a3..8ed0f06984 100644 --- a/substrate/substrate/network/src/on_demand.rs +++ b/substrate/substrate/network/src/on_demand.rs @@ -102,7 +102,7 @@ impl Future for RemoteCallResponse { impl OnDemand where E: service::ExecuteInContext, - B::Header: HeaderT, + B::Header: HeaderT, { /// Creates new on-demand service. pub fn new(checker: Arc>) -> Self { @@ -166,7 +166,7 @@ impl OnDemand where impl OnDemandService for OnDemand where B: BlockT, E: service::ExecuteInContext, - B::Header: HeaderT, + B::Header: HeaderT, { fn on_connect(&self, peer: PeerId, role: service::Role) { if !role.intersects(service::Role::FULL | service::Role::AUTHORITY) { // TODO: correct? @@ -210,7 +210,7 @@ impl OnDemandService for OnDemand where impl Fetcher for OnDemand where B: BlockT, E: service::ExecuteInContext, - B::Header: HeaderT, + B::Header: HeaderT, { type RemoteCallResult = RemoteCallResponse; @@ -223,8 +223,8 @@ impl Fetcher for OnDemand where impl OnDemandCore where B: BlockT, - E: service::ExecuteInContext , - B::Header: HeaderT + E: service::ExecuteInContext, + B::Header: HeaderT, { pub fn add_peer(&mut self, peer: PeerId) { self.idle_peers.push_back(peer); diff --git a/substrate/substrate/network/src/protocol.rs b/substrate/substrate/network/src/protocol.rs index 93985c85fe..c0f5f2eabb 100644 --- a/substrate/substrate/network/src/protocol.rs +++ b/substrate/substrate/network/src/protocol.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use std::time; use parking_lot::RwLock; use serde_json; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashFor}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashFor, As}; use runtime_primitives::generic::BlockId; use network::PeerId; @@ -191,7 +191,7 @@ pub(crate) struct ContextData { chain: Arc>, } -impl> Protocol where B::Header: HeaderT { +impl> Protocol { /// Create a new instance. pub fn new( config: ProtocolConfig, @@ -349,12 +349,12 @@ impl> Protocol where B::Header: HeaderT id = BlockId::Number(number + 1), + message::Direction::Ascending => id = BlockId::Number(number + As::sa(1)), message::Direction::Descending => { - if number == 0 { + if number == As::sa(0) { break; } - id = BlockId::Number(number - 1) + id = BlockId::Number(number - As::sa(1)) } } } diff --git a/substrate/substrate/network/src/service.rs b/substrate/substrate/network/src/service.rs index 82825c750b..20d4bf3219 100644 --- a/substrate/substrate/network/src/service.rs +++ b/substrate/substrate/network/src/service.rs @@ -31,7 +31,7 @@ use chain::Client; use message::LocalizedBftMessage; use specialization::Specialization; use on_demand::OnDemandService; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; +use runtime_primitives::traits::{Block as BlockT}; /// Type that represents fetch completion future. pub type FetchFuture = oneshot::Receiver>; @@ -135,7 +135,7 @@ pub struct Params { } /// Polkadot network service. Handles network IO and manages connectivity. -pub struct Service> where B::Header: HeaderT { +pub struct Service> { /// Network service network: NetworkService, /// Devp2p protocol handler @@ -144,7 +144,7 @@ pub struct Service> where B::Header: H protocol_id: ProtocolId, } -impl> Service where B::Header: HeaderT { +impl> Service { /// Creates and register protocol with the network service pub fn new(params: Params, protocol_id: ProtocolId) -> Result>, Error> { let service = NetworkService::new(params.network_config.clone(), None)?; @@ -209,13 +209,13 @@ impl> Service where B::Header: H } } -impl> Drop for Service where B::Header: HeaderT { +impl> Drop for Service { fn drop(&mut self) { self.stop(); } } -impl> ExecuteInContext for Service where B::Header: HeaderT { +impl> ExecuteInContext for Service { fn execute_in_context)>(&self, closure: F) { self.network.with_context(self.protocol_id, |context| { closure(&mut ProtocolContext::new(self.handler.protocol.context_data(), &mut NetSyncIo::new(context))) @@ -223,7 +223,7 @@ impl> ExecuteInContext for Service< } } -impl> SyncProvider for Service where B::Header: HeaderT { +impl> SyncProvider for Service { /// Get sync status fn status(&self) -> ProtocolStatus { self.handler.protocol.status() @@ -257,7 +257,7 @@ impl> SyncProvider for Service> NetworkProtocolHandler for ProtocolHandler where B::Header: HeaderT { +impl> NetworkProtocolHandler for ProtocolHandler { fn initialize(&self, io: &NetworkContext) { io.register_timer(TICK_TOKEN, TICK_TIMEOUT) .expect("Error registering sync timer"); @@ -304,7 +304,7 @@ pub trait ManageNetwork: Send + Sync { } -impl> ManageNetwork for Service where B::Header: HeaderT { +impl> ManageNetwork for Service { fn accept_unreserved_peers(&self) { self.network.set_non_reserved_mode(NonReservedPeerMode::Accept); } diff --git a/substrate/substrate/network/src/sync.rs b/substrate/substrate/network/src/sync.rs index 5d23460521..878016b985 100644 --- a/substrate/substrate/network/src/sync.rs +++ b/substrate/substrate/network/src/sync.rs @@ -19,7 +19,7 @@ use protocol::Context; use network::PeerId; use client::{ImportResult, BlockStatus, ClientInfo}; use blocks::{self, BlockCollection}; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor}; use runtime_primitives::generic::BlockId; use message::{self, generic::Message as GenericMessage}; use service::Role; @@ -29,17 +29,17 @@ const MAX_BLOCKS_TO_REQUEST: usize = 128; struct PeerSync { pub common_hash: B::Hash, - pub common_number: ::Number, + pub common_number: NumberFor, pub best_hash: B::Hash, - pub best_number: ::Number, + pub best_number: NumberFor, pub state: PeerSyncState, } #[derive(Copy, Clone, Eq, PartialEq, Debug)] enum PeerSyncState { - AncestorSearch(::Number), + AncestorSearch(NumberFor), Available, - DownloadingNew(::Number), + DownloadingNew(NumberFor), DownloadingStale(B::Hash), } @@ -48,7 +48,7 @@ pub struct ChainSync { genesis_hash: B::Hash, peers: HashMap>, blocks: BlockCollection, - best_queued_number: u64, + best_queued_number: NumberFor, best_queued_hash: B::Hash, required_block_attributes: Vec, } @@ -68,12 +68,10 @@ pub struct Status { /// Current global sync state. pub state: SyncState, /// Target sync block number. - pub best_seen_block: Option<::Number>, + pub best_seen_block: Option>, } -impl ChainSync where - B::Header: HeaderT, -{ +impl ChainSync { /// Create a new instance. pub(crate) fn new(role: Role, info: &ClientInfo) -> Self { let mut required_block_attributes = vec![ @@ -94,7 +92,7 @@ impl ChainSync where } } - fn best_seen_block(&self) -> Option { + fn best_seen_block(&self) -> Option> { self.peers.values().max_by_key(|p| p.best_number).map(|p| p.best_number) } @@ -102,7 +100,7 @@ impl ChainSync where pub(crate) fn status(&self) -> Status { let best_seen = self.best_seen_block(); let state = match &best_seen { - &Some(n) if n > self.best_queued_number && n - self.best_queued_number > 5 => SyncState::Downloading, + &Some(n) if n > self.best_queued_number && n - self.best_queued_number > As::sa(5) => SyncState::Downloading, _ => SyncState::Idle, }; Status { @@ -123,17 +121,17 @@ impl ChainSync where debug!(target:"sync", "New peer with known bad best block {} ({}).", info.best_hash, info.best_number); protocol.disable_peer(peer_id); }, - (Ok(BlockStatus::Unknown), 0) => { + (Ok(BlockStatus::Unknown), b) if b == As::sa(0) => { debug!(target:"sync", "New peer with unknown genesis hash {} ({}).", info.best_hash, info.best_number); protocol.disable_peer(peer_id); }, (Ok(BlockStatus::Unknown), _) => { let our_best = self.best_queued_number; - if our_best > 0 { + if our_best > As::sa(0) { debug!(target:"sync", "New peer with unknown best hash {} ({}), searching for common ancestor.", info.best_hash, info.best_number); self.peers.insert(peer_id, PeerSync { common_hash: self.genesis_hash, - common_number: 0, + common_number: As::sa(0), best_hash: info.best_hash, best_number: info.best_number, state: PeerSyncState::AncestorSearch(our_best), @@ -144,7 +142,7 @@ impl ChainSync where debug!(target:"sync", "New peer with best hash {} ({}).", info.best_hash, info.best_number); self.peers.insert(peer_id, PeerSync { common_hash: self.genesis_hash, - common_number: 0, + common_number: As::sa(0), best_hash: info.best_hash, best_number: info.best_number, state: PeerSyncState::Available, @@ -176,7 +174,7 @@ impl ChainSync where peer.state = PeerSyncState::Available; self.blocks.insert(start_block, response.blocks, peer_id); - self.blocks.drain(self.best_queued_number + 1) + self.blocks.drain(self.best_queued_number + As::sa(1)) }, PeerSyncState::DownloadingStale(_) => { peer.state = PeerSyncState::Available; @@ -199,9 +197,9 @@ impl ChainSync where trace!(target:"sync", "Found common ancestor for peer {}: {} ({})", peer_id, block.hash, n); vec![] }, - Ok(our_best) if n > 0 => { + Ok(our_best) if n > As::sa(0) => { trace!(target:"sync", "Ancestry block mismatch for peer {}: theirs: {} ({}), ours: {:?}", peer_id, block.hash, n, our_best); - let n = n - 1; + let n = n - As::sa(1); peer.state = PeerSyncState::AncestorSearch(n); Self::request_ancestry(protocol, peer_id, n); return; @@ -315,7 +313,7 @@ impl ChainSync where } } - fn block_imported(&mut self, hash: &B::Hash, number: u64) { + fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { if number > self.best_queued_number { self.best_queued_number = number; self.best_queued_hash = *hash; @@ -392,7 +390,7 @@ impl ChainSync where Err(e) => { debug!(target:"sync", "Error reading blockchain: {:?}", e); self.best_queued_hash = self.genesis_hash; - self.best_queued_number = 0; + self.best_queued_number = As::sa(0); } } } @@ -437,7 +435,7 @@ impl ChainSync where from: message::FromBlock::Number(range.start), to: None, direction: message::Direction::Ascending, - max: Some((range.end - range.start) as u32), + max: Some((range.end - range.start).as_() as u32), }; peer.state = PeerSyncState::DownloadingNew(range.start); protocol.send_message(peer_id, GenericMessage::BlockRequest(request)); @@ -450,7 +448,7 @@ impl ChainSync where } } - fn request_ancestry(protocol: &mut Context, peer_id: PeerId, block: u64) { + fn request_ancestry(protocol: &mut Context, peer_id: PeerId, block: NumberFor) { trace!(target: "sync", "Requesting ancestry block #{} from {}", block, peer_id); let request = message::generic::BlockRequest { id: 0, diff --git a/substrate/substrate/rpc/src/author/mod.rs b/substrate/substrate/rpc/src/author/mod.rs index 1d916b4e29..327e67b5dc 100644 --- a/substrate/substrate/rpc/src/author/mod.rs +++ b/substrate/substrate/rpc/src/author/mod.rs @@ -24,7 +24,6 @@ use codec::Slicable; use primitives::Bytes; use runtime_primitives::{generic, traits::Block as BlockT}; -use state_machine; pub mod error; @@ -64,7 +63,6 @@ impl AuthorApi for Author wh B: client::backend::Backend + Send + Sync + 'static, E: client::CallExecutor + Send + Sync + 'static, Block: BlockT + 'static, - client::error::Error: From<<>::State as state_machine::backend::Backend>::Error>, P: ExtrinsicPool, Hash>, P::Error: 'static, Ex: Slicable, diff --git a/substrate/substrate/rpc/src/author/tests.rs b/substrate/substrate/rpc/src/author/tests.rs index 6c5b5fb05e..acdb320e96 100644 --- a/substrate/substrate/rpc/src/author/tests.rs +++ b/substrate/substrate/rpc/src/author/tests.rs @@ -17,7 +17,7 @@ use super::*; use std::{fmt, sync::Arc}; -use extrinsic_pool::api; +use extrinsic_pool::{api, txpool}; use test_client; use parking_lot::Mutex; @@ -55,6 +55,14 @@ impl api::ExtrinsicPool for DummyTxPool { Err(Error) } } + + fn light_status(&self) -> txpool::LightStatus { + unreachable!() + } + + fn import_notification_stream(&self) -> api::EventStream { + unreachable!() + } } #[test] diff --git a/substrate/substrate/rpc/src/chain/mod.rs b/substrate/substrate/rpc/src/chain/mod.rs index 1227b5ef97..d1d055b5c0 100644 --- a/substrate/substrate/rpc/src/chain/mod.rs +++ b/substrate/substrate/rpc/src/chain/mod.rs @@ -21,7 +21,6 @@ use std::sync::Arc; use runtime_primitives::traits::Block as BlockT; use runtime_primitives::generic::BlockId; use client::{self, Client, BlockchainEvents}; -use state_machine; use jsonrpc_macros::pubsub; use jsonrpc_pubsub::SubscriptionId; @@ -84,7 +83,6 @@ impl ChainApi for Chain wh Block: BlockT + 'static, B: client::backend::Backend + Send + Sync + 'static, E: client::CallExecutor + Send + Sync + 'static, - client::error::Error: From<<>::State as state_machine::backend::Backend>::Error>, { type Metadata = ::metadata::Metadata; diff --git a/substrate/substrate/rpc/src/state/mod.rs b/substrate/substrate/rpc/src/state/mod.rs index 9e48fae762..40d6f70f6c 100644 --- a/substrate/substrate/rpc/src/state/mod.rs +++ b/substrate/substrate/rpc/src/state/mod.rs @@ -28,7 +28,6 @@ use runtime_primitives::generic::BlockId; use runtime_primitives::traits::Block as BlockT; use primitives::storage::{StorageKey, StorageData}; use primitives::hexdisplay::HexDisplay; -use state_machine; use self::error::Result; @@ -73,7 +72,6 @@ impl StateApi for Arc> where Block: BlockT + 'static, B: client::backend::Backend + Send + Sync + 'static, E: CallExecutor + Send + Sync + 'static, - client::error::Error: From<<>::State as state_machine::backend::Backend>::Error>, { fn storage_at(&self, key: StorageKey, block: Block::Hash) -> Result { trace!(target: "rpc", "Querying storage at {:?} for key {}", block, HexDisplay::from(&key.0)); diff --git a/substrate/substrate/runtime/primitives/src/traits.rs b/substrate/substrate/runtime/primitives/src/traits.rs index 77eaba3fd2..289e10a388 100644 --- a/substrate/substrate/runtime/primitives/src/traits.rs +++ b/substrate/substrate/runtime/primitives/src/traits.rs @@ -372,6 +372,8 @@ pub trait Block: Clone + Send + Sync + Slicable + Eq + MaybeSerializeDebug + 'st /// Extract the hashing type for a block. pub type HashFor = <::Header as Header>::Hashing; +/// Extract the number type for a block. +pub type NumberFor = <::Header as Header>::Number; /// A "checkable" piece of information, used by the standard Substrate Executive in order to /// check the validity of a piece of extrinsic information, usually by verifying the signature. diff --git a/substrate/substrate/service/Cargo.toml b/substrate/substrate/service/Cargo.toml new file mode 100644 index 0000000000..91985a550b --- /dev/null +++ b/substrate/substrate/service/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "substrate-service" +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" +tokio = "0.1.7" +exit-future = "0.1" +serde = "1.0" +serde_json = "1.0" +serde_derive = "1.0" +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-executor = { path = "../../substrate/executor" } +substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" } +substrate-telemetry = { path = "../../substrate/telemetry" } diff --git a/substrate/substrate/service/src/chain_spec.rs b/substrate/substrate/service/src/chain_spec.rs new file mode 100644 index 0000000000..ccf0943054 --- /dev/null +++ b/substrate/substrate/service/src/chain_spec.rs @@ -0,0 +1,151 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Substrate chain configurations. + +use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; +use primitives::storage::{StorageKey, StorageData}; +use runtime_primitives::{BuildStorage, StorageMap}; +use serde_json as json; +use components::RuntimeGenesis; + +enum GenesisSource { + File(PathBuf), + Embedded(&'static [u8]), + Factory(fn() -> G), +} + +impl GenesisSource { + fn resolve(&self) -> Result, String> { + #[derive(Serialize, Deserialize)] + struct GenesisContainer { + genesis: Genesis, + } + + 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(Genesis::Runtime(f())), + } + } +} + +impl<'a, G: RuntimeGenesis> 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()), + } + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +enum Genesis { + Runtime(G), + Raw(HashMap), +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct ChainSpecFile { + pub name: String, + pub boot_nodes: Vec, +} + +/// A configuration of a chain. Can be used to build a genesis block. +pub struct ChainSpec { + spec: ChainSpecFile, + genesis: GenesisSource, +} + +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), + }) + } + + /// Create hardcoded spec. + pub fn from_genesis(name: &str, constructor: fn() -> G, boot_nodes: Vec) -> Self { + let spec = ChainSpecFile { + name: name.to_owned(), + boot_nodes: boot_nodes, + }; + ChainSpec { + spec, + genesis: GenesisSource::Factory(constructor), + } + } + + /// 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)) + } +} diff --git a/substrate/substrate/service/src/components.rs b/substrate/substrate/service/src/components.rs new file mode 100644 index 0000000000..dac2bd1da7 --- /dev/null +++ b/substrate/substrate/service/src/components.rs @@ -0,0 +1,238 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Polkadot service components. + +use std::sync::Arc; +use std::marker::PhantomData; +use serde::{Serialize, de::DeserializeOwned}; +use chain_spec::ChainSpec; +use client_db; +use client::{self, Client}; +use error; +use network::{self, OnDemand}; +use substrate_executor::{NativeExecutor, NativeExecutionDispatch}; +use extrinsic_pool::{txpool::Options as ExtrinsicPoolOptions, api::ExtrinsicPool as ExtrinsicPoolApi}; +use runtime_primitives::{traits::Block as BlockT, generic::BlockId, BuildStorage}; +use config::Configuration; + +// Type aliases. +// These exist mainly to avoid typing `::Foo` all over the code. +/// Network service type for a factory. +pub type NetworkService = network::Service< + ::Block, + ::NetworkProtocol +>; + +/// Code executor type for a factory. +pub type CodeExecutor = NativeExecutor<::RuntimeDispatch>; + +/// Full client backend type for a factory. +pub type FullBackend = client_db::Backend<::Block>; + +/// Full client executor type for a factory. +pub type FullExecutor = client::LocalCallExecutor< + client_db::Backend<::Block>, + CodeExecutor +>; + +/// Light client backend type for a factory. +pub type LightBackend = client::light::backend::Backend< + client_db::light::LightStorage<::Block>, + network::OnDemand<::Block, + NetworkService> +>; + +/// Light client executor type for a factory. +pub type LightExecutor = client::light::call_executor::RemoteCallExecutor< + client::light::blockchain::Blockchain< + client_db::light::LightStorage<::Block>, + network::OnDemand<::Block, NetworkService> + >, + network::OnDemand<::Block, NetworkService> +>; + +/// Full client type for a factory. +pub type FullClient = Client, FullExecutor, ::Block>; + +/// Light client type for a factory. +pub type LightClient = Client, LightExecutor, ::Block>; + +/// `ChainSpec` specialization for a factory. +pub type FactoryChainSpec = ChainSpec<::Genesis>; + +/// `Genesis` specialization for a factory. +pub type FactoryGenesis = ::Genesis; + +/// `Block` type for a factory. +pub type FactoryBlock = ::Block; + +/// Client type for `Components`. +pub type ComponentClient = Client< + ::Backend, + ::Executor, + FactoryBlock<::Factory> +>; + +/// Block type for `Components` +pub type ComponentBlock = <::Factory as ServiceFactory>::Block; + +/// Extrinsic pool API type for `Components`. +pub type PoolApi = <::ExtrinsicPool as ExtrinsicPool>>::Api; + +/// A set of traits for the runtime genesis config. +pub trait RuntimeGenesis: Serialize + DeserializeOwned + BuildStorage {} +impl RuntimeGenesis for T {} + +/// A collection of types and methods to build a service on top of the substrate service. +pub trait ServiceFactory { + /// Block type. + type Block: BlockT; + /// Network protocol extensions. + type NetworkProtocol: network::specialization::Specialization + Default; + /// Chain runtime. + type RuntimeDispatch: NativeExecutionDispatch + Send + Sync + 'static; + /// Extrinsic pool type for the full client. + type FullExtrinsicPool: ExtrinsicPool; + /// Extrinsic pool type for the light client. + type LightExtrinsicPool: ExtrinsicPool; + /// Genesis configuration for the runtime. + type Genesis: RuntimeGenesis; + /// Network protocol id. + const NETWORK_PROTOCOL_ID: network::ProtocolId; + + //TODO: replace these with a constructor trait. that ExtrinsicPool implements. + /// Extrinsic pool constructor for the full client. + fn build_full_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result; + /// Extrinsic pool constructor for the light client. + fn build_light_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result; +} + +// TODO: move this to substrate-extrinsic-pool +/// Extrinsic pool bridge. +pub trait ExtrinsicPool: network::TransactionPool + Send + Sync + 'static { + type Api: ExtrinsicPoolApi, Block::Hash>; + + /// Update the pool after a new block has been imported. + fn prune_imported(&self, hash: &Block::Hash); + /// Returns underlying API. + fn api(&self) -> Arc; +} + +/// A collection of types and function to generalise over full / light client type. +pub trait Components { + /// Associated service factory. + type Factory: ServiceFactory; + /// Client backend. + type Backend: 'static + client::backend::Backend>; + /// Client executor. + type Executor: 'static + client::CallExecutor> + Send + Sync; + /// Extrinsic pool type. + type ExtrinsicPool: ExtrinsicPool>; + + /// Create client. + fn build_client( + config: &Configuration>, + executor: CodeExecutor, + ) + -> Result<( + Arc>, + Option, NetworkService>>> + ), error::Error>; + + /// Create extrinsic pool. + fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result; +} + +/// A struct that implement `Components` for the full client. +pub struct FullComponents { + _factory: PhantomData, +} + +impl Components for FullComponents { + type Factory = Factory; + type Executor = FullExecutor; + type Backend = FullBackend; + type ExtrinsicPool = ::FullExtrinsicPool; + + fn build_client( + config: &Configuration>, + executor: CodeExecutor, + ) + -> Result<( + Arc>, + Option, NetworkService>>> + ), error::Error> + { + let db_settings = client_db::DatabaseSettings { + cache_size: None, + path: config.database_path.as_str().into(), + pruning: config.pruning.clone(), + }; + Ok((Arc::new(client_db::new_client(db_settings, executor, &config.chain_spec, config.execution_strategy)?), None)) + } + + fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result + { + Factory::build_full_extrinsic_pool(config, client) + } +} + +/// A struct that implement `Components` for the light client. +pub struct LightComponents { + _factory: PhantomData, +} + +impl Components for LightComponents { + type Factory = Factory; + type Executor = LightExecutor; + type Backend = LightBackend; + type ExtrinsicPool = ::LightExtrinsicPool; + + fn build_client( + config: &Configuration>, + executor: CodeExecutor, + ) + -> Result<( + Arc>, + Option, + NetworkService>>> + ), error::Error> + { + let db_settings = client_db::DatabaseSettings { + cache_size: None, + path: config.database_path.as_str().into(), + pruning: config.pruning.clone(), + }; + 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(), &config.chain_spec)?; + Ok((Arc::new(client), Some(fetcher))) + } + + fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result + { + Factory::build_light_extrinsic_pool(config, client) + } +} diff --git a/substrate/polkadot/service/src/config.rs b/substrate/substrate/service/src/config.rs similarity index 69% rename from substrate/polkadot/service/src/config.rs rename to substrate/substrate/service/src/config.rs index c9fdc9ced1..9f47bf0c44 100644 --- a/substrate/polkadot/service/src/config.rs +++ b/substrate/substrate/service/src/config.rs @@ -1,34 +1,36 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate 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, +// Substrate 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 .? +// along with Substrate. If not, see .? //! Service configuration. -use transaction_pool; +use extrinsic_pool; use chain_spec::ChainSpec; -pub use state_machine::ExecutionStrategy; +pub use client::ExecutionStrategy; pub use network::Role; pub use network::NetworkConfiguration; pub use client_db::PruningMode; +use runtime_primitives::BuildStorage; +use serde::{Serialize, de::DeserializeOwned}; /// Service configuration. -pub struct Configuration { +pub struct Configuration { /// Node roles. pub roles: Role, - /// Transaction pool configuration. - pub transaction_pool: transaction_pool::Options, + /// Extrinsic pool configuration. + pub extrinsic_pool: extrinsic_pool::txpool::Options, /// Network configuration. pub network: NetworkConfiguration, /// Path to key files. @@ -40,7 +42,7 @@ pub struct Configuration { /// Additional key seeds. pub keys: Vec, /// Chain configuration. - pub chain_spec: ChainSpec, + pub chain_spec: ChainSpec, /// Telemetry server URL, optional - only `Some` if telemetry reporting is enabled pub telemetry: Option, /// Node name. @@ -49,14 +51,14 @@ pub struct Configuration { pub execution_strategy: ExecutionStrategy, } -impl Configuration { +impl Configuration { /// Create default config for given chain spec. - pub fn default_with_spec(chain_spec: ChainSpec) -> Configuration { + 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(), + extrinsic_pool: Default::default(), network: Default::default(), keystore_path: Default::default(), database_path: Default::default(), diff --git a/substrate/polkadot/service/src/error.rs b/substrate/substrate/service/src/error.rs similarity index 78% rename from substrate/polkadot/service/src/error.rs rename to substrate/substrate/service/src/error.rs index fbb6981407..f3c3ff3811 100644 --- a/substrate/polkadot/service/src/error.rs +++ b/substrate/substrate/service/src/error.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate 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, +// Substrate 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 . +// along with Substrate. If not, see . //! Errors that can occur during the service operation. diff --git a/substrate/substrate/service/src/lib.rs b/substrate/substrate/service/src/lib.rs new file mode 100644 index 0000000000..7e525f2d62 --- /dev/null +++ b/substrate/substrate/service/src/lib.rs @@ -0,0 +1,211 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Substrate service. Starts a thread that spins the network, the client and the extrinsic pool. +//! Manages communication between them. + +#![warn(unused_extern_crates)] + +extern crate futures; +extern crate exit_future; +extern crate serde; +extern crate serde_json; +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_executor; +extern crate substrate_client as client; +extern crate substrate_client_db as client_db; +extern crate substrate_extrinsic_pool as extrinsic_pool; +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; + +mod components; +mod error; +mod config; +mod chain_spec; + +use std::sync::Arc; +use futures::prelude::*; +use keystore::Store as Keystore; +use client::BlockchainEvents; +use network::ManageNetwork; +use runtime_primitives::traits::{Header, As}; +use exit_future::Signal; +use tokio::runtime::TaskExecutor; + +pub use self::error::{ErrorKind, Error}; +pub use config::{Configuration, Role, PruningMode}; +pub use chain_spec::ChainSpec; +pub use extrinsic_pool::txpool::{Options as ExtrinsicPoolOptions}; +pub use extrinsic_pool::api::{ExtrinsicPool as ExtrinsicPoolApi}; + +pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend, + LightExecutor, ExtrinsicPool, Components, PoolApi, ComponentClient, + ComponentBlock, FullClient, LightClient, FullComponents, LightComponents, + CodeExecutor, NetworkService, FactoryChainSpec, FactoryBlock, RuntimeGenesis, +}; + +/// Substrate service. +pub struct Service { + client: Arc>, + network: Arc>, + extrinsic_pool: Arc, + signal: Option, +} + +/// Creates bare client without any networking. +pub fn new_client(config: Configuration) + -> Result>>, error::Error> +{ + let executor = components::CodeExecutor::::default(); + let (client, _) = components::FullComponents::::build_client( + &config, + executor, + )?; + Ok(client) +} + +impl Service + where + Components: components::Components, +{ + /// Creates a new service. + pub fn new( + config: Configuration<::Genesis>, + task_executor: TaskExecutor + ) + -> Result + { + let (signal, exit) = ::exit_future::signal(); + + // Create client + let executor = components::CodeExecutor::::default(); + + let mut keystore = Keystore::open(config.keystore_path.as_str().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 (client, on_demand) = Components::build_client(&config, executor)?; + let best_header = client.best_block_header()?; + + info!("Best block: #{}", best_header.number()); + telemetry!("node.start"; "height" => best_header.number().as_(), "best" => ?best_header.hash()); + + let extrinsic_pool = Arc::new( + Components::build_extrinsic_pool(config.extrinsic_pool, client.clone())? + ); + let extrinsic_pool_adapter = extrinsic_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: extrinsic_pool_adapter, + specialization: ::NetworkProtocol::default(), + }; + + let network = network::Service::new(network_params, Components::Factory::NETWORK_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 = extrinsic_pool.clone(); + + let events = client.import_notification_stream() + .for_each(move |notification| { + network.on_block_imported(notification.hash, ¬ification.header); + txpool.prune_imported(¬ification.hash); + Ok(()) + }) + .select(exit.clone()) + .then(|_| Ok(())); + task_executor.spawn(events); + } + + { + // extrinsic notifications + let network = network.clone(); + let events = extrinsic_pool.api().import_notification_stream() + // TODO [ToDr] Consider throttling? + .for_each(move |_| { + network.trigger_repropagate(); + Ok(()) + }) + .select(exit.clone()) + .then(|_| Ok(())); + + task_executor.spawn(events); + } + + Ok(Service { + client: client, + network: network, + extrinsic_pool: extrinsic_pool, + signal: Some(signal), + }) + } + + /// Get shared client instance. + pub fn client(&self) -> Arc> { + self.client.clone() + } + + /// Get shared network instance. + pub fn network(&self) -> Arc> { + self.network.clone() + } + + /// Get shared extrinsic pool instance. + pub fn extrinsic_pool(&self) -> Arc> { + self.extrinsic_pool.api() + } +} + +impl Drop for Service where Components: components::Components { + fn drop(&mut self) { + debug!(target: "service", "Substrate service shutdown"); + + self.network.stop_network(); + + if let Some(signal) = self.signal.take() { + signal.fire(); + } + } +}