Files
pezkuwi-subxt/substrate/node/cli/src/service.rs
T
Svyatoslav Nikolsky 616716cb4b GRANDPA finality proof draft (#1268)
* grandpa finality proof

* prove GrandpaApi::grandpa_authorities using parent block + some docs

* create justification when consensus data is changed

* generate justifications periodically

* test for ConsensusChanges
2019-01-11 19:25:03 +01:00

204 lines
7.4 KiB
Rust

// 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 std::time::Duration;
use client;
use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration, NothingExtra};
use grandpa;
use node_executor;
use primitives::ed25519::Pair;
use node_primitives::{Block, InherentData};
use node_runtime::{GenesisConfig, RuntimeApi};
use substrate_service::{
FactoryFullConfiguration, LightComponents, FullComponents, FullBackend,
FullClient, LightClient, LightBackend, FullExecutor, LightExecutor, TaskExecutor
};
use transaction_pool::{self, txpool::{Pool as TransactionPool}};
construct_simple_protocol! {
/// Demo protocol attachment for substrate.
pub struct NodeProtocol where Block = Block { }
}
/// Node specific configuration
pub struct NodeConfig<F: substrate_service::ServiceFactory> {
/// grandpa connection to import block
// FIXME: rather than putting this on the config, let's have an actual intermediate setup state
// https://github.com/paritytech/substrate/issues/1134
pub grandpa_import_setup: Option<(Arc<grandpa::BlockImportForService<F>>, grandpa::LinkHalfForService<F>)>,
}
impl<F> Default for NodeConfig<F> where F: substrate_service::ServiceFactory {
fn default() -> NodeConfig<F> {
NodeConfig {
grandpa_import_setup: None,
}
}
}
construct_service_factory! {
struct Factory {
Block = Block,
RuntimeApi = RuntimeApi,
NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) },
RuntimeDispatch = node_executor::Executor,
FullTransactionPoolApi = transaction_pool::ChainApi<client::Client<FullBackend<Self>, FullExecutor<Self>, Block, RuntimeApi>, Block>
{ |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
LightTransactionPoolApi = transaction_pool::ChainApi<client::Client<LightBackend<Self>, LightExecutor<Self>, Block, RuntimeApi>, Block>
{ |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
Genesis = GenesisConfig,
Configuration = NodeConfig<Self>,
FullService = FullComponents<Self>
{ |config: FactoryFullConfiguration<Self>, executor: TaskExecutor|
FullComponents::<Factory>::new(config, executor) },
AuthoritySetup = {
|mut service: Self::FullService, executor: TaskExecutor, local_key: Option<Arc<Pair>>| {
let (block_import, link_half) = service.config.custom.grandpa_import_setup.take()
.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
if let Some(ref key) = local_key {
info!("Using authority key {}", key.public());
let proposer = Arc::new(substrate_basic_authorship::ProposerFactory {
client: service.client(),
transaction_pool: service.transaction_pool(),
});
let client = service.client();
executor.spawn(start_aura(
SlotDuration::get_or_compute(&*client)?,
key.clone(),
client,
block_import.clone(),
proposer,
service.network(),
service.on_exit(),
));
info!("Running Grandpa session as Authority {}", key.public());
}
executor.spawn(grandpa::run_grandpa(
grandpa::Config {
local_key,
gossip_duration: Duration::new(4, 0), // FIXME: make this available through chainspec?
justification_period: 4096,
name: Some(service.config.name.clone())
},
link_half,
grandpa::NetworkBridge::new(service.network()),
service.on_exit(),
)?);
Ok(service)
}
},
LightService = LightComponents<Self>
{ |config, executor| <LightComponents<Factory>>::new(config, executor) },
FullImportQueue = AuraImportQueue<
Self::Block,
grandpa::BlockImportForService<Self>,
NothingExtra,
::consensus::InherentProducingFn<InherentData>,
>
{ |config: &mut FactoryFullConfiguration<Self> , client: Arc<FullClient<Self>>| {
let slot_duration = SlotDuration::get_or_compute(&*client)?;
let (block_import, link_half) = grandpa::block_import::<_, _, _, RuntimeApi, FullClient<Self>>(client.clone(), client)?;
let block_import = Arc::new(block_import);
config.custom.grandpa_import_setup = Some((block_import.clone(), link_half));
Ok(import_queue(
slot_duration,
block_import,
NothingExtra,
::consensus::make_basic_inherent as _,
))
}},
LightImportQueue = AuraImportQueue<
Self::Block,
LightClient<Self>,
NothingExtra,
::consensus::InherentProducingFn<InherentData>,
>
{ |ref mut config, client: Arc<LightClient<Self>>|
Ok(import_queue(
SlotDuration::get_or_compute(&*client)?,
client,
NothingExtra,
::consensus::make_basic_inherent as _,
))
},
}
}
#[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 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,
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,
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);
}
}