Service factory refactor (#3382)

* Move Service::new to a macro

* Move function calls to macros

* Extract offchain_workers and start_rpc in separate function

In follow-up commits, we want to be able to directly call maintain_transaction_pool, offchain_workers, and start_rpc, without having to implement the Components trait.
This commit is a preliminary step: we extract the code to freestanding functions.

* Introduce an AbstractService trait

* Introduce NewService as an implementation detail of Service

* Implement traits on NewService instead

Instead of implementing AbstractService, Future, and Executor on Service, we implement them on NewService instead.

The implementations of AbstractService, Future, and Executor on Service still exist, but they just wrap to the respective implementations for NewService.

* Move components creation back to macro invocation

Instead of having multiple $build_ parameters passed to the macro, let's group them all into one.

This change is necessary for the follow-up commits, because we are going to call new_impl! only after all the components have already been built.

* Add a $block parameter to new_impl

This makes it possible to be explicit as what the generic parameter of the NewServiceis, without relying on type inference.

* Introduce the ServiceBuilder struct

Introduces a new builder-like ServiceBuilder struct that creates a NewService.

* Macro-ify import_blocks, export_blocks and revert_chain

Similar to the introduction of new_impl!, we extract the actual code into a macro, letting us get rid of the Components and Factory traits

* Add export_blocks, import_blocks and revert_chain methods on ServiceBuilder

Can be used as a replacement for the chain_ops::* methods

* Add run_with_builder

Instead of just run, adds run_with_builder to ParseAndPrepareExport/Import/Revert. This lets you run these operations with a ServiceBuilder instead of a ServiceFactory.

* Transition node and node-template to ServiceBuilder

* Transition transaction-factory to the new service factory

This is technically a breaking change, but the transaction-factory crate is only ever used from within substrate-node, which this commit updates as well.

* Remove old service factory

* Adjust the AbstractService trait to be more usable

We slightly change the trait bounds in order to make all the methods usable.

* Make substrate-service-test compile

* Fix the node-cli tests

* Remove the old API

* Remove the components module

* Fix indentation on chain_ops

* Line widths

* Fix bad line widths commit

* Line widths again 🤦

* Fix the sync test

* Apply suggestions from code review

Co-Authored-By: Gavin Wood <i@gavwood.com>

* Address some concerns

* Remove TelemetryOnConnect

* Remove informant::start

* Update jsonrpc

* Rename factory to builder

* Line widths 😩
This commit is contained in:
Pierre Krieger
2019-08-27 11:18:41 +02:00
committed by Bastian Köcher
parent 144bd228af
commit 5b8ebf7baf
26 changed files with 1960 additions and 2263 deletions
+65 -100
View File
@@ -16,44 +16,19 @@
//! Chain utilities.
use std::{self, io::{Read, Write, Seek}};
use futures::prelude::*;
use futures03::TryFutureExt as _;
use log::{info, warn};
use sr_primitives::generic::{SignedBlock, BlockId};
use sr_primitives::traits::{SaturatedConversion, Zero, One, Block, Header, NumberFor};
use consensus_common::import_queue::{ImportQueue, IncomingBlock, Link, BlockImportError, BlockImportResult};
use network::message;
use consensus_common::BlockOrigin;
use crate::components::{self, Components, ServiceFactory, FactoryFullConfiguration, FactoryBlockNumber, RuntimeGenesis};
use crate::new_client;
use codec::{Decode, Encode, IoReader};
use crate::RuntimeGenesis;
use crate::error;
use crate::chain_spec::ChainSpec;
/// Export a range of blocks to a binary stream.
pub fn export_blocks<F, E, W>(
config: FactoryFullConfiguration<F>,
exit: E,
mut output: W,
from: FactoryBlockNumber<F>,
to: Option<FactoryBlockNumber<F>>,
json: bool
) -> error::Result<()>
where
F: ServiceFactory,
E: Future<Item=(),Error=()> + Send + 'static,
W: Write,
{
let client = new_client::<F>(&config)?;
let mut block = from;
#[macro_export]
macro_rules! export_blocks {
($client:ident, $exit:ident, $output:ident, $from:ident, $to:ident, $json:ident) => {{
let mut block = $from;
let last = match to {
let last = match $to {
Some(v) if v.is_zero() => One::one(),
Some(v) => v,
None => client.info().chain.best_number,
None => $client.info().chain.best_number,
};
if last < block {
@@ -62,28 +37,28 @@ pub fn export_blocks<F, E, W>(
let (exit_send, exit_recv) = std::sync::mpsc::channel();
::std::thread::spawn(move || {
let _ = exit.wait();
let _ = $exit.wait();
let _ = exit_send.send(());
});
info!("Exporting blocks from #{} to #{}", block, last);
if !json {
if !$json {
let last_: u64 = last.saturated_into::<u64>();
let block_: u64 = block.saturated_into::<u64>();
let len: u64 = last_ - block_ + 1;
output.write(&len.encode())?;
$output.write(&len.encode())?;
}
loop {
if exit_recv.try_recv().is_ok() {
break;
}
match client.block(&BlockId::number(block))? {
match $client.block(&BlockId::number(block))? {
Some(block) => {
if json {
serde_json::to_writer(&mut output, &block)
if $json {
serde_json::to_writer(&mut $output, &block)
.map_err(|e| format!("Error writing JSON: {}", e))?;
} else {
output.write(&block.encode())?;
$output.write(&block.encode())?;
}
},
None => break,
@@ -97,66 +72,59 @@ pub fn export_blocks<F, E, W>(
block += One::one();
}
Ok(())
}}
}
struct WaitLink {
imported_blocks: u64,
has_error: bool,
}
#[macro_export]
macro_rules! import_blocks {
($block:ty, $client:ident, $queue:ident, $exit:ident, $input:ident) => {{
use consensus_common::import_queue::{IncomingBlock, Link, BlockImportError, BlockImportResult};
use consensus_common::BlockOrigin;
use network::message;
use sr_primitives::generic::SignedBlock;
use sr_primitives::traits::Block;
use futures03::TryFutureExt as _;
impl WaitLink {
fn new() -> WaitLink {
WaitLink {
imported_blocks: 0,
has_error: false,
}
struct WaitLink {
imported_blocks: u64,
has_error: bool,
}
}
impl<B: Block> Link<B> for WaitLink {
fn blocks_processed(
&mut self,
imported: usize,
_count: usize,
results: Vec<(Result<BlockImportResult<NumberFor<B>>, BlockImportError>, B::Hash)>
) {
self.imported_blocks += imported as u64;
for result in results {
if let (Err(err), hash) = result {
warn!("There was an error importing block with hash {:?}: {:?}", hash, err);
self.has_error = true;
break;
impl WaitLink {
fn new() -> WaitLink {
WaitLink {
imported_blocks: 0,
has_error: false,
}
}
}
}
/// Returns a future that import blocks from a binary stream.
pub fn import_blocks<F, E, R>(
mut config: FactoryFullConfiguration<F>,
exit: E,
input: R
) -> error::Result<impl Future<Item = (), Error = ()>>
where F: ServiceFactory, E: Future<Item=(),Error=()> + Send + 'static, R: Read + Seek,
{
let client = new_client::<F>(&config)?;
// FIXME #1134 this shouldn't need a mutable config.
let select_chain = components::FullComponents::<F>::build_select_chain(&mut config, client.clone())?;
let (mut queue, _) = components::FullComponents::<F>::build_import_queue(
&mut config,
client.clone(),
select_chain,
None,
)?;
impl<B: Block> Link<B> for WaitLink {
fn blocks_processed(
&mut self,
imported: usize,
_count: usize,
results: Vec<(Result<BlockImportResult<NumberFor<B>>, BlockImportError>, B::Hash)>
) {
self.imported_blocks += imported as u64;
for result in results {
if let (Err(err), hash) = result {
warn!("There was an error importing block with hash {:?}: {:?}", hash, err);
self.has_error = true;
break;
}
}
}
}
let (exit_send, exit_recv) = std::sync::mpsc::channel();
::std::thread::spawn(move || {
let _ = exit.wait();
let _ = $exit.wait();
let _ = exit_send.send(());
});
let mut io_reader_input = IoReader(input);
let mut io_reader_input = IoReader($input);
let count: u64 = Decode::decode(&mut io_reader_input)
.map_err(|e| format!("Error reading file: {}", e))?;
info!("Importing {} blocks", count);
@@ -165,11 +133,11 @@ pub fn import_blocks<F, E, R>(
if exit_recv.try_recv().is_ok() {
break;
}
match SignedBlock::<F::Block>::decode(&mut io_reader_input) {
match SignedBlock::<$block>::decode(&mut io_reader_input) {
Ok(signed) => {
let (header, extrinsics) = signed.block.deconstruct();
let hash = header.hash();
let block = message::BlockData::<F::Block> {
let block = message::BlockData::<$block> {
hash,
justification: signed.justification,
header: Some(header),
@@ -178,8 +146,8 @@ pub fn import_blocks<F, E, R>(
message_queue: None
};
// import queue handles verification and importing it into the client
queue.import_blocks(BlockOrigin::File, vec![
IncomingBlock::<F::Block> {
$queue.import_blocks(BlockOrigin::File, vec![
IncomingBlock::<$block> {
hash: block.hash,
header: block.header,
body: block.body,
@@ -208,7 +176,7 @@ pub fn import_blocks<F, E, R>(
let blocks_before = link.imported_blocks;
let _ = futures03::future::poll_fn(|cx| {
queue.poll_actions(cx, &mut link);
$queue.poll_actions(cx, &mut link);
std::task::Poll::Pending::<Result<(), ()>>
}).compat().poll();
if link.has_error {
@@ -226,24 +194,20 @@ pub fn import_blocks<F, E, R>(
);
}
if link.imported_blocks >= count {
info!("Imported {} blocks. Best: #{}", block_count, client.info().chain.best_number);
info!("Imported {} blocks. Best: #{}", block_count, $client.info().chain.best_number);
Ok(Async::Ready(()))
} else {
Ok(Async::NotReady)
}
}))
}}
}
/// Revert the chain.
pub fn revert_chain<F>(
config: FactoryFullConfiguration<F>,
blocks: FactoryBlockNumber<F>
) -> error::Result<()>
where F: ServiceFactory,
{
let client = new_client::<F>(&config)?;
let reverted = client.revert(blocks)?;
let info = client.info().chain;
#[macro_export]
macro_rules! revert_chain {
($client:ident, $blocks:ident) => {{
let reverted = $client.revert($blocks)?;
let info = $client.info().chain;
if reverted.is_zero() {
info!("There aren't any non-finalized blocks to revert.");
@@ -251,6 +215,7 @@ pub fn revert_chain<F>(
info!("Reverted {} blocks. Best: #{} ({})", reverted, info.best_number, info.best_hash);
}
Ok(())
}}
}
/// Build a chain spec json