Remove node-service (#933)

* Move `chain_spec` into `node-cli`

* Fixes tests

* Adds `construct_simple_service` and `construct_service_factory` macros

* Remove the `node-service` crate

* Add some documentation

* Fixes compilation on stable
This commit is contained in:
Bastian Köcher
2018-10-19 12:22:32 +02:00
committed by Gav Wood
parent ca38fd72f6
commit 7f6862ba5e
11 changed files with 379 additions and 365 deletions
+13 -1
View File
@@ -9,4 +9,16 @@ log = "0.4"
tokio = "0.1.7"
exit-future = "0.1"
substrate-cli = { path = "../../core/cli" }
node-service = { path = "../service" }
substrate-primitives = { path = "../../core/primitives" }
node-runtime = { path = "../runtime" }
node-primitives = { path = "../primitives" }
node-network = { path = "../network" }
hex-literal = "0.1"
substrate-service = { path = "../../core/service" }
substrate-transaction-pool = { path = "../../core/transaction-pool" }
substrate-network = { path = "../../core/network" }
sr-primitives = { path = "../../core/sr-primitives" }
node-executor = { path = "../executor" }
[dev-dependencies]
substrate-service-test = { path = "../../core/service/test" }
@@ -20,11 +20,14 @@ use primitives::{AuthorityId, ed25519};
use node_runtime::{GenesisConfig, ConsensusConfig, CouncilSeatsConfig, CouncilVotingConfig, DemocracyConfig,
SessionConfig, StakingConfig, TimestampConfig, BalancesConfig, TreasuryConfig,
ContractConfig, Permill, Perbill};
use service::ChainSpec;
use substrate_service;
const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
pub fn bbq_birch_config() -> Result<ChainSpec<GenesisConfig>, String> {
/// Specialised `ChainSpec`.
pub type ChainSpec = substrate_service::ChainSpec<GenesisConfig>;
pub fn bbq_birch_config() -> Result<ChainSpec, String> {
ChainSpec::from_embedded(include_bytes!("../res/bbq-birch.json"))
}
@@ -122,7 +125,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
}
/// Staging testnet config.
pub fn staging_testnet_config() -> ChainSpec<GenesisConfig> {
pub fn staging_testnet_config() -> ChainSpec {
let boot_nodes = vec![
];
ChainSpec::from_genesis(
@@ -227,7 +230,7 @@ fn development_config_genesis() -> GenesisConfig {
}
/// Development config (single validator Alice)
pub fn development_config() -> ChainSpec<GenesisConfig> {
pub fn development_config() -> ChainSpec {
ChainSpec::from_genesis("Development", "development", development_config_genesis, vec![], None, None, None)
}
@@ -238,18 +241,30 @@ fn local_testnet_genesis() -> GenesisConfig {
])
}
fn local_testnet_genesis_instant() -> GenesisConfig {
let mut genesis = local_testnet_genesis();
genesis.timestamp = Some(TimestampConfig { period: 0 });
genesis
}
/// Local testnet config (multivalidator Alice + Bob)
pub fn local_testnet_config() -> ChainSpec<GenesisConfig> {
pub fn local_testnet_config() -> ChainSpec {
ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![], None, None, None)
}
/// Local testnet config (multivalidator Alice + Bob)
pub fn integration_test_config() -> ChainSpec<GenesisConfig> {
ChainSpec::from_genesis("Integration Test", "test", local_testnet_genesis_instant, vec![], None, None, None)
#[cfg(test)]
mod tests {
use super::*;
use service_test;
use service::Factory;
fn local_testnet_genesis_instant() -> GenesisConfig {
let mut genesis = local_testnet_genesis();
genesis.timestamp = Some(TimestampConfig { period: 0 });
genesis
}
/// Local testnet config (multivalidator Alice + Bob)
pub fn integration_test_config() -> ChainSpec {
ChainSpec::from_genesis("Integration Test", "test", local_testnet_genesis_instant, vec![], None, None, None)
}
#[test]
fn test_connectivity() {
service_test::connectivity::<Factory>(integration_test_config());
}
}
+25 -10
View File
@@ -22,17 +22,32 @@
extern crate tokio;
extern crate substrate_cli as cli;
extern crate node_service as service;
extern crate substrate_primitives as primitives;
extern crate node_runtime;
extern crate exit_future;
#[macro_use]
extern crate hex_literal;
#[cfg(test)]
extern crate substrate_service_test as service_test;
extern crate substrate_transaction_pool as transaction_pool;
extern crate substrate_network as network;
extern crate node_network;
extern crate sr_primitives as runtime_primitives;
extern crate node_primitives;
#[macro_use]
extern crate substrate_service;
extern crate node_executor;
#[macro_use]
extern crate log;
pub use cli::error;
mod chain_spec;
mod service;
use tokio::runtime::Runtime;
pub use service::{Components as ServiceComponents, Service, CustomConfiguration, ServiceFactory};
pub use cli::{VersionInfo, IntoExit};
use substrate_service::{ServiceFactory, Roles as ServiceRoles};
/// The chain specification option.
#[derive(Clone, Debug)]
@@ -49,12 +64,12 @@ pub enum ChainSpec {
/// Get a chain config from a spec setting.
impl ChainSpec {
pub(crate) fn load(self) -> Result<service::ChainSpec, String> {
pub(crate) fn load(self) -> Result<chain_spec::ChainSpec, String> {
Ok(match self {
ChainSpec::BbqBirch => service::chain_spec::bbq_birch_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::BbqBirch => chain_spec::bbq_birch_config()?,
ChainSpec::Development => chain_spec::development_config(),
ChainSpec::LocalTestnet => chain_spec::local_testnet_config(),
ChainSpec::StagingTestnet => chain_spec::staging_testnet_config(),
})
}
@@ -69,7 +84,7 @@ impl ChainSpec {
}
}
fn load_spec(id: &str) -> Result<Option<service::ChainSpec>, String> {
fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
Ok(match ChainSpec::from(id) {
Some(spec) => Some(spec.load()?),
None => None,
@@ -93,7 +108,7 @@ pub fn run<I, T, E>(args: I, exit: E, version: cli::VersionInfo) -> error::Resul
info!("Roles: {:?}", config.roles);
let mut runtime = Runtime::new()?;
let executor = runtime.executor();
match config.roles == service::Roles::LIGHT {
match config.roles == ServiceRoles::LIGHT {
true => run_until_exit(&mut runtime, service::Factory::new_light(config, executor)?, exit)?,
false => run_until_exit(&mut runtime, service::Factory::new_full(config, executor)?, exit)?,
}
@@ -108,7 +123,7 @@ fn run_until_exit<C, E>(
e: E,
) -> error::Result<()>
where
C: service::Components,
C: substrate_service::Components,
E: IntoExit,
{
let (exit_send, exit) = exit_future::signal();
+133
View File
@@ -0,0 +1,133 @@
// Copyright 2018 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 <http://www.gnu.org/licenses/>.
#![warn(unused_extern_crates)]
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
use std::sync::Arc;
use transaction_pool::{self, txpool::{Pool as TransactionPool}};
use node_primitives::Block;
use node_runtime::GenesisConfig;
use node_network::Protocol as DemoProtocol;
use substrate_service::{
FactoryFullConfiguration, LightComponents, FullComponents, FullBackend,
LightBackend, FullExecutor, LightExecutor
};
use network::import_queue::{BasicQueue, BlockOrigin, ImportBlock, Verifier};
use runtime_primitives::{traits::Block as BlockT};
use primitives::AuthorityId;
use node_executor;
// TODO: Remove me, when we have a functional consensus.
/// A verifier that doesn't actually do any checks
pub struct NoneVerifier;
/// This Verifiyer accepts all data as valid
impl<B: BlockT> Verifier<B> for NoneVerifier {
fn verify(
&self,
origin: BlockOrigin,
header: B::Header,
justification: Vec<u8>,
body: Option<Vec<B::Extrinsic>>
) -> Result<(ImportBlock<B>, Option<Vec<AuthorityId>>), String> {
Ok((ImportBlock {
origin,
header,
body,
finalized: true,
external_justification: justification,
internal_justification: vec![],
auxiliary: Vec::new(),
}, None))
}
}
construct_simple_service!(Service);
construct_service_factory! {
struct Factory {
Block = Block,
NetworkProtocol = DemoProtocol { |config| Ok(DemoProtocol::new()) },
RuntimeDispatch = node_executor::Executor,
FullTransactionPoolApi = transaction_pool::ChainApi<FullBackend<Self>, FullExecutor<Self>, Block>
{ |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
LightTransactionPoolApi = transaction_pool::ChainApi<LightBackend<Self>, LightExecutor<Self>, Block>
{ |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
Genesis = GenesisConfig,
Configuration = (),
FullService = Service<FullComponents<Self>>
{ |config, executor| Service::<FullComponents<Factory>>::new(config, executor) },
LightService = Service<LightComponents<Self>>
{ |config, executor| Service::<LightComponents<Factory>>::new(config, executor) },
ImportQueue = BasicQueue<Block, NoneVerifier>
{ |_, _| Ok(BasicQueue::new(Arc::new(NoneVerifier {}))) }
{ |_, _| Ok(BasicQueue::new(Arc::new(NoneVerifier {}))) },
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "rhd")]
fn test_sync() {
use {service_test, Factory};
use client::{ImportBlock, BlockOrigin};
let alice: Arc<ed25519::Pair> = Arc::new(Keyring::Alice.into());
let bob: Arc<ed25519::Pair> = Arc::new(Keyring::Bob.into());
let validators = vec![alice.public().0.into(), bob.public().0.into()];
let keys: Vec<&ed25519::Pair> = vec![&*alice, &*bob];
let offline = Arc::new(RwLock::new(OfflineTracker::new()));
let dummy_runtime = ::tokio::runtime::Runtime::new().unwrap();
let block_factory = |service: &<Factory as service::ServiceFactory>::FullService| {
let block_id = BlockId::number(service.client().info().unwrap().chain.best_number);
let parent_header = service.client().header(&block_id).unwrap().unwrap();
let consensus_net = ConsensusNetwork::new(service.network(), service.client().clone());
let proposer_factory = consensus::ProposerFactory {
client: service.client().clone(),
transaction_pool: service.transaction_pool().clone(),
network: consensus_net,
offline: offline.clone(),
force_delay: 0,
handle: dummy_runtime.executor(),
};
let (proposer, _, _) = proposer_factory.init(&parent_header, &validators, alice.clone()).unwrap();
let block = proposer.propose().expect("Error making test block");
ImportBlock {
origin: BlockOrigin::File,
external_justification: Vec::new(),
internal_justification: Vec::new(),
finalized: true,
body: Some(block.extrinsics),
header: block.header,
auxiliary: Vec::new(),
}
};
let extrinsic_factory = |service: &<Factory as service::ServiceFactory>::FullService| {
let payload = (0, Call::Balances(BalancesCall::transfer(RawAddress::Id(bob.public().0.into()), 69.into())), Era::immortal(), service.client().genesis_hash());
let signature = alice.sign(&payload.encode()).into();
let id = alice.public().0.into();
let xt = UncheckedExtrinsic {
signature: Some((RawAddress::Id(id), signature, payload.0, Era::immortal())),
function: payload.1,
}.encode();
let v: Vec<u8> = Decode::decode(&mut xt.as_slice()).unwrap();
OpaqueExtrinsic(v)
};
service_test::sync::<Factory, _, _>(chain_spec::integration_test_config(), block_factory, extrinsic_factory);
}
}
-33
View File
@@ -1,33 +0,0 @@
[package]
name = "node-service"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
error-chain = "0.12"
hex-literal = "0.1"
lazy_static = "1.0"
log = "0.4"
node-consensus = { path = "../consensus" }
node-executor = { path = "../executor" }
node-network = { path = "../network" }
node-primitives = { path = "../primitives" }
node-runtime = { path = "../runtime" }
parity-codec = { version = "2.1" }
parking_lot = "0.4"
slog = "^2"
sr-io = { path = "../../core/sr-io" }
sr-primitives = { path = "../../core/sr-primitives" }
substrate-client = { path = "../../core/client" }
substrate-network = { path = "../../core/network" }
substrate-primitives = { path = "../../core/primitives" }
substrate-service = { path = "../../core/service" }
substrate-telemetry = { path = "../../core/telemetry" }
substrate-transaction-pool = { path = "../../core/transaction-pool" }
tokio = "0.1.7"
[dev-dependencies]
substrate-service-test = { path = "../../core/service/test" }
substrate-test-client = { path = "../../core/test-client" }
substrate-keyring = { path = "../../core/keyring" }
rhododendron = "0.3"
-265
View File
@@ -1,265 +0,0 @@
// Copyright 2018 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 <http://www.gnu.org/licenses/>.
#![warn(unused_extern_crates)]
//! Substrate service. Specialized wrapper over substrate service.
extern crate node_primitives;
extern crate node_runtime;
extern crate node_executor;
extern crate node_network;
extern crate substrate_client as client;
extern crate substrate_network as network;
extern crate substrate_primitives as primitives;
extern crate substrate_service as service;
extern crate substrate_transaction_pool as transaction_pool;
extern crate tokio;
#[cfg(test)]
extern crate substrate_service_test as service_test;
#[macro_use]
extern crate hex_literal;
#[cfg(all(test, feature="rhd"))]
extern crate rhododendron as rhd;
extern crate sr_primitives as runtime_primitives;
pub mod chain_spec;
use std::sync::Arc;
use transaction_pool::txpool::{Pool as TransactionPool};
use node_primitives::{Block, Hash};
use node_runtime::GenesisConfig;
use client::Client;
use node_network::Protocol as DemoProtocol;
use tokio::runtime::TaskExecutor;
use service::FactoryFullConfiguration;
use network::import_queue::{BasicQueue, BlockOrigin, ImportBlock, Verifier};
use runtime_primitives::{traits::Block as BlockT};
use primitives::{Blake2Hasher, AuthorityId};
pub use service::{Roles, PruningMode, TransactionPoolOptions, ServiceFactory,
ErrorKind, Error, ComponentBlock, LightComponents, FullComponents};
pub use client::ExecutionStrategy;
/// Specialised `ChainSpec`.
pub type ChainSpec = service::ChainSpec<GenesisConfig>;
/// Client type for specialised `Components`.
pub type ComponentClient<C> = Client<<C as Components>::Backend, <C as Components>::Executor, Block>;
pub type NetworkService = network::Service<Block, <Factory as service::ServiceFactory>::NetworkProtocol, Hash>;
/// A verifier that doesn't actually do any checks
pub struct NoneVerifier;
/// This Verifiyer accepts all data as valid
impl<B: BlockT> Verifier<B> for NoneVerifier {
fn verify(
&self,
origin: BlockOrigin,
header: B::Header,
justification: Vec<u8>,
body: Option<Vec<B::Extrinsic>>
) -> Result<(ImportBlock<B>, Option<Vec<AuthorityId>>), String> {
Ok((ImportBlock {
origin,
header,
body,
finalized: true,
external_justification: justification,
internal_justification: vec![],
auxiliary: Vec::new(),
}, None))
}
}
/// A collection of type to generalise specific components over full / light client.
pub trait Components: service::Components {
/// Demo API.
type Api: 'static + Send + Sync;
/// Client backend.
type Backend: 'static + client::backend::Backend<Block, Blake2Hasher>;
/// Client executor.
type Executor: 'static + client::CallExecutor<Block, Blake2Hasher> + Send + Sync;
}
impl Components for service::LightComponents<Factory> {
type Api = service::LightClient<Factory>;
type Executor = service::LightExecutor<Factory>;
type Backend = service::LightBackend<Factory>;
}
impl Components for service::FullComponents<Factory> {
type Api = service::FullClient<Factory>;
type Executor = service::FullExecutor<Factory>;
type Backend = service::FullBackend<Factory>;
}
/// All configuration for the node.
pub type Configuration = FactoryFullConfiguration<Factory>;
/// Demo-specific configuration.
#[derive(Default)]
pub struct CustomConfiguration;
/// Config for the substrate service.
pub struct Factory;
impl service::ServiceFactory for Factory {
type Block = Block;
type ExtrinsicHash = Hash;
type NetworkProtocol = DemoProtocol;
type RuntimeDispatch = node_executor::Executor;
type FullTransactionPoolApi = transaction_pool::ChainApi<service::FullBackend<Self>, service::FullExecutor<Self>, Block>;
type LightTransactionPoolApi = transaction_pool::ChainApi<service::LightBackend<Self>, service::LightExecutor<Self>, Block>;
type Genesis = GenesisConfig;
type Configuration = CustomConfiguration;
type FullService = Service<service::FullComponents<Self>>;
type LightService = Service<service::LightComponents<Self>>;
/// instance of import queue for clients
type ImportQueue = BasicQueue<Block, NoneVerifier>;
fn build_full_transaction_pool(config: TransactionPoolOptions, client: Arc<service::FullClient<Self>>)
-> Result<TransactionPool<Self::FullTransactionPoolApi>, Error>
{
Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client)))
}
fn build_light_transaction_pool(config: TransactionPoolOptions, client: Arc<service::LightClient<Self>>)
-> Result<TransactionPool<Self::LightTransactionPoolApi>, Error>
{
Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client)))
}
fn build_network_protocol(_config: &Configuration)
-> Result<DemoProtocol, Error>
{
Ok(DemoProtocol::new())
}
fn build_full_import_queue(
_config: &FactoryFullConfiguration<Self>,
_client: Arc<service::FullClient<Self>>,
) -> Result<BasicQueue<Block, NoneVerifier>, service::Error> {
Ok(BasicQueue::new(Arc::new(NoneVerifier {})))
}
fn build_light_import_queue(
_config: &FactoryFullConfiguration<Self>,
_client: Arc<service::LightClient<Self>>,
) -> Result<BasicQueue<Block, NoneVerifier>, service::Error> {
Ok(BasicQueue::new(Arc::new(NoneVerifier {})))
}
fn new_light(config: Configuration, executor: TaskExecutor)
-> Result<Service<LightComponents<Factory>>, Error>
{
let service = service::Service::<LightComponents<Factory>>::new(config, executor.clone())?;
Ok(Service {
inner: service,
_consensus: None,
})
}
fn new_full(config: Configuration, executor: TaskExecutor)
-> Result<Service<FullComponents<Factory>>, Error>
{
let service = service::Service::<FullComponents<Factory>>::new(config, executor.clone())?;
// FIXME: Spin consensus service if configured
let consensus = None;
Ok(Service {
inner: service,
_consensus: consensus,
})
}
}
/// Demo service.
pub struct Service<C: Components> {
inner: service::Service<C>,
_consensus: Option<bool>, // FIXME: add actual consensus engine
}
impl<C: Components> ::std::ops::Deref for Service<C> {
type Target = service::Service<C>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
/// Creates bare client without any networking.
pub fn new_client(config: Configuration)
-> Result<Arc<service::ComponentClient<FullComponents<Factory>>>, Error>
{
service::new_client::<Factory>(&config)
}
#[cfg(test)]
mod tests {
use {service_test, Factory, chain_spec};
#[test]
fn test_connectivity() {
service_test::connectivity::<Factory>(chain_spec::integration_test_config());
}
#[test]
#[cfg(feature = "rhd")]
fn test_sync() {
use client::{ImportBlock, BlockOrigin};
let alice: Arc<ed25519::Pair> = Arc::new(Keyring::Alice.into());
let bob: Arc<ed25519::Pair> = Arc::new(Keyring::Bob.into());
let validators = vec![alice.public().0.into(), bob.public().0.into()];
let keys: Vec<&ed25519::Pair> = vec![&*alice, &*bob];
let offline = Arc::new(RwLock::new(OfflineTracker::new()));
let dummy_runtime = ::tokio::runtime::Runtime::new().unwrap();
let block_factory = |service: &<Factory as service::ServiceFactory>::FullService| {
let block_id = BlockId::number(service.client().info().unwrap().chain.best_number);
let parent_header = service.client().header(&block_id).unwrap().unwrap();
let consensus_net = ConsensusNetwork::new(service.network(), service.client().clone());
let proposer_factory = consensus::ProposerFactory {
client: service.client().clone(),
transaction_pool: service.transaction_pool().clone(),
network: consensus_net,
offline: offline.clone(),
force_delay: 0,
handle: dummy_runtime.executor(),
};
let (proposer, _, _) = proposer_factory.init(&parent_header, &validators, alice.clone()).unwrap();
let block = proposer.propose().expect("Error making test block");
ImportBlock {
origin: BlockOrigin::File,
external_justification: Vec::new(),
internal_justification: Vec::new(),
finalized: true,
body: Some(block.extrinsics),
header: block.header,
auxiliary: Vec::new(),
}
};
let extrinsic_factory = |service: &<Factory as service::ServiceFactory>::FullService| {
let payload = (0, Call::Balances(BalancesCall::transfer(RawAddress::Id(bob.public().0.into()), 69.into())), Era::immortal(), service.client().genesis_hash());
let signature = alice.sign(&payload.encode()).into();
let id = alice.public().0.into();
let xt = UncheckedExtrinsic {
signature: Some((RawAddress::Id(id), signature, payload.0, Era::immortal())),
function: payload.1,
}.encode();
let v: Vec<u8> = Decode::decode(&mut xt.as_slice()).unwrap();
OpaqueExtrinsic(v)
};
service_test::sync::<Factory, _, _>(chain_spec::integration_test_config(), block_factory, extrinsic_factory);
}
}