Support both polkadot and kusama runtimes (#704)

* Allow both polkadot and kusama runtimes

* Allow both polkadot and kusama runtimes

* Make `collator` build

* Removed kusama runtime

* Introduced common runtime

* Updated for latest substrate

* Updated CI targets

* Updated CI version check

* Removed unused dependency

* Pulled latests substrate

* Pulled latest substrate

* Fixed version

* Apply suggestions from code review

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* NEW_HEADS_IDENTIFIER moved to primitives

* Updated CI check script

* Fixed script

* Set epoch duration for polkadot

* ci: check_runtime for both runtimes

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: gabriel klawitter <gabreal@users.noreply.github.com>
This commit is contained in:
Arkadiy Paronyan
2020-01-03 19:31:39 +01:00
committed by Gavin Wood
parent 9a9bbd1c2d
commit a00d74d825
42 changed files with 1954 additions and 646 deletions
+2 -2
View File
@@ -11,15 +11,15 @@ client = { package = "sc-client", git = "https://github.com/paritytech/substrate
client-api = { package = "sc-client-api", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
primitives = { package = "sp-core", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
sc-network = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
consensus_common = { package = "sp-consensus", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
polkadot-runtime = { path = "../runtime" }
polkadot-primitives = { path = "../primitives" }
polkadot-cli = { path = "../cli" }
polkadot-network = { path = "../network" }
polkadot-validation = { path = "../validation" }
polkadot-service = { path = "../service" }
log = "0.4.8"
tokio = "0.2.1"
tokio = "0.1.22"
futures-timer = "1.0"
[dev-dependencies]
+173 -186
View File
@@ -51,7 +51,7 @@ use std::time::Duration;
use std::pin::Pin;
use futures::{future, Future, Stream, FutureExt, TryFutureExt, StreamExt, task::Spawn};
use log::{warn, error};
use log::warn;
use client::BlockchainEvents;
use primitives::{Pair, Blake2Hasher};
use polkadot_primitives::{
@@ -62,13 +62,12 @@ use polkadot_primitives::{
}
};
use polkadot_cli::{
Worker, IntoExit, ProvideRuntimeApi, AbstractService, CustomConfiguration, ParachainHost,
ProvideRuntimeApi, AbstractService, ParachainHost, IsKusama, WrappedExecutor,
service::{self, Roles, SelectChain}
};
use polkadot_network::validation::{LeafWorkParams, ValidationNetwork};
use polkadot_network::PolkadotProtocol;
use polkadot_runtime::RuntimeApi;
pub use polkadot_cli::VersionInfo;
pub use polkadot_cli::{VersionInfo, load_spec, service::Configuration};
pub use polkadot_network::validation::Incoming;
pub use polkadot_validation::SignedStatement;
pub use polkadot_primitives::parachain::CollatorId;
@@ -129,7 +128,7 @@ impl<R: fmt::Display> fmt::Display for Error<R> {
}
/// The Polkadot client type.
pub type PolkadotClient<B, E> = client::Client<B, E, Block, RuntimeApi>;
pub type PolkadotClient<B, E, R> = client::Client<B, E, Block, R>;
/// Something that can build a `ParachainContext`.
pub trait BuildParachainContext {
@@ -137,9 +136,9 @@ pub trait BuildParachainContext {
type ParachainContext: self::ParachainContext;
/// Build the `ParachainContext`.
fn build<B, E, SP>(
fn build<B, E, R, SP>(
self,
client: Arc<PolkadotClient<B, E>>,
client: Arc<PolkadotClient<B, E, R>>,
spawner: SP,
network: Arc<dyn Network>,
) -> Result<Self::ParachainContext, ()>
@@ -266,205 +265,179 @@ impl<P: 'static, E: 'static, SP: 'static> RelayChainContext for ApiContext<P, E,
}
}
struct CollationNode<P, E> {
build_parachain_context: P,
/// Run the collator node using the given `service`.
fn run_collator_node<S, E, P>(
service: S,
exit: E,
para_id: ParaId,
key: Arc<CollatorPair>,
}
impl<P, E> IntoExit for CollationNode<P, E> where
E: futures::Future<Output=()> + Unpin + Send + 'static
{
type Exit = E;
fn into_exit(self) -> Self::Exit {
self.exit
}
}
impl<P, E> Worker for CollationNode<P, E> where
P: BuildParachainContext + Send + 'static,
P::ParachainContext: Send + 'static,
<P::ParachainContext as ParachainContext>::ProduceCandidate: Send + 'static,
E: futures::Future<Output=()> + Clone + Unpin + Send + Sync + 'static,
{
type Work = Box<dyn Future<Output=()> + Unpin + Send>;
fn configuration(&self) -> CustomConfiguration {
let mut config = CustomConfiguration::default();
config.collating_for = Some((
self.key.public(),
self.para_id,
));
config
}
fn work<S, SC, B, CE, SP>(self, service: &S, spawner: SP) -> Self::Work
build_parachain_context: P,
) -> polkadot_cli::error::Result<()>
where
S: AbstractService<
Block = Block,
RuntimeApi = RuntimeApi,
Backend = B,
SelectChain = SC,
NetworkSpecialization = PolkadotProtocol,
CallExecutor = CE,
>,
SC: polkadot_service::SelectChain<Block> + 'static,
B: client_api::backend::Backend<Block, Blake2Hasher> + 'static,
CE: client::CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync + 'static,
SP: Spawn + Clone + Send + Sync + 'static,
{
let CollationNode { build_parachain_context, exit, para_id, key } = self;
let client = service.client();
let network = service.network();
let known_oracle = client.clone();
let select_chain = if let Some(select_chain) = service.select_chain() {
select_chain
} else {
error!("The node cannot work because it can't select chain.");
return Box::new(future::ready(()));
};
S: AbstractService<Block = service::Block, NetworkSpecialization = service::PolkadotProtocol>,
client::Client<S::Backend, S::CallExecutor, service::Block, S::RuntimeApi>: ProvideRuntimeApi,
<client::Client<S::Backend, S::CallExecutor, service::Block, S::RuntimeApi> as ProvideRuntimeApi>::Api:
ParachainHost<service::Block, Error = sp_blockchain::Error>,
// Rust bug: https://github.com/rust-lang/rust/issues/24159
S::Backend: service::Backend<service::Block, service::Blake2Hasher>,
// Rust bug: https://github.com/rust-lang/rust/issues/24159
S::CallExecutor: service::CallExecutor<service::Block, service::Blake2Hasher>,
// Rust bug: https://github.com/rust-lang/rust/issues/24159
S::SelectChain: service::SelectChain<service::Block>,
E: futures::Future<Output=()> + Clone + Unpin + Send + Sync + 'static,
P: BuildParachainContext,
P::ParachainContext: Send + 'static,
<P::ParachainContext as ParachainContext>::ProduceCandidate: Send,
{
let runtime = tokio::runtime::Runtime::new().map_err(|e| format!("{:?}", e))?;
let spawner = WrappedExecutor(service.spawn_task_handle());
let is_known = move |block_hash: &Hash| {
use consensus_common::BlockStatus;
use polkadot_network::gossip::Known;
let client = service.client();
let network = service.network();
let known_oracle = client.clone();
let select_chain = if let Some(select_chain) = service.select_chain() {
select_chain
} else {
return Err(polkadot_cli::error::Error::Other("The node cannot work because it can't select chain.".into()))
};
match known_oracle.block_status(&BlockId::hash(*block_hash)) {
Err(_) | Ok(BlockStatus::Unknown) | Ok(BlockStatus::Queued) => None,
Ok(BlockStatus::KnownBad) => Some(Known::Bad),
Ok(BlockStatus::InChainWithState) | Ok(BlockStatus::InChainPruned) =>
match select_chain.leaves() {
Err(_) => None,
Ok(leaves) => if leaves.contains(block_hash) {
Some(Known::Leaf)
} else {
Some(Known::Old)
},
}
}
};
let is_known = move |block_hash: &Hash| {
use consensus_common::BlockStatus;
use polkadot_network::gossip::Known;
let message_validator = polkadot_network::gossip::register_validator(
network.clone(),
(is_known, client.clone()),
&spawner
);
match known_oracle.block_status(&BlockId::hash(*block_hash)) {
Err(_) | Ok(BlockStatus::Unknown) | Ok(BlockStatus::Queued) => None,
Ok(BlockStatus::KnownBad) => Some(Known::Bad),
Ok(BlockStatus::InChainWithState) | Ok(BlockStatus::InChainPruned) =>
match select_chain.leaves() {
Err(_) => None,
Ok(leaves) => if leaves.contains(block_hash) {
Some(Known::Leaf)
} else {
Some(Known::Old)
},
}
}
};
let validation_network = Arc::new(ValidationNetwork::new(
message_validator,
exit.clone(),
client.clone(),
spawner.clone(),
));
let message_validator = polkadot_network::gossip::register_validator(
network.clone(),
(is_known, client.clone()),
&spawner,
);
let parachain_context = match build_parachain_context.build(
client.clone(),
spawner,
validation_network.clone(),
) {
Ok(ctx) => ctx,
Err(()) => {
error!("Could not build the parachain context!");
return Box::new(future::ready(()))
}
};
let validation_network = Arc::new(ValidationNetwork::new(
message_validator,
exit.clone(),
client.clone(),
spawner.clone(),
));
let inner_exit = exit.clone();
let work = client.import_notification_stream()
.for_each(move |notification| {
macro_rules! try_fr {
($e:expr) => {
match $e {
Ok(x) => x,
Err(e) => return future::Either::Left(future::err(Error::Polkadot(
format!("{:?}", e)
))),
}
let parachain_context = match build_parachain_context.build(
client.clone(),
spawner,
validation_network.clone(),
) {
Ok(ctx) => ctx,
Err(()) => {
return Err(polkadot_cli::error::Error::Other("Could not build the parachain context!".into()))
}
};
let inner_exit = exit.clone();
let work = client.import_notification_stream()
.for_each(move |notification| {
macro_rules! try_fr {
($e:expr) => {
match $e {
Ok(x) => x,
Err(e) => return future::Either::Left(future::err(Error::Polkadot(
format!("{:?}", e)
))),
}
}
}
let relay_parent = notification.hash;
let id = BlockId::hash(relay_parent);
let relay_parent = notification.hash;
let id = BlockId::hash(relay_parent);
let network = network.clone();
let client = client.clone();
let key = key.clone();
let parachain_context = parachain_context.clone();
let validation_network = validation_network.clone();
let inner_exit_2 = inner_exit.clone();
let network = network.clone();
let client = client.clone();
let key = key.clone();
let parachain_context = parachain_context.clone();
let validation_network = validation_network.clone();
let inner_exit_2 = inner_exit.clone();
let work = future::lazy(move |_| {
let api = client.runtime_api();
let status = match try_fr!(api.parachain_status(&id, para_id)) {
Some(status) => status,
None => return future::Either::Left(future::ok(())),
};
let work = future::lazy(move |_| {
let api = client.runtime_api();
let status = match try_fr!(api.parachain_status(&id, para_id)) {
Some(status) => status,
None => return future::Either::Left(future::ok(())),
};
let validators = try_fr!(api.validators(&id));
let validators = try_fr!(api.validators(&id));
let targets = compute_targets(
para_id,
validators.as_slice(),
try_fr!(api.duty_roster(&id)),
);
let context = ApiContext {
network: validation_network,
parent_hash: relay_parent,
validators,
};
let collation_work = collate(
relay_parent,
para_id,
status,
context,
parachain_context,
key,
).map_ok(move |(collation, outgoing)| {
network.with_spec(move |spec, ctx| {
let res = spec.add_local_collation(
ctx,
relay_parent,
targets,
collation,
outgoing,
);
let exit = inner_exit_2.clone();
tokio::spawn(future::select(res.boxed(), exit).map(drop));
})
});
future::Either::Right(collation_work)
});
let deadlined = future::select(
work,
futures_timer::Delay::new(COLLATION_TIMEOUT)
let targets = compute_targets(
para_id,
validators.as_slice(),
try_fr!(api.duty_roster(&id)),
);
let silenced = deadlined
.map(|either| {
if let future::Either::Right(_) = either {
warn!("Collation failure: timeout");
}
});
let context = ApiContext {
network: validation_network,
parent_hash: relay_parent,
validators,
};
let collation_work = collate(
relay_parent,
para_id,
status,
context,
parachain_context,
key,
).map_ok(move |(collation, outgoing)| {
network.with_spec(move |spec, ctx| {
let res = spec.add_local_collation(
ctx,
relay_parent,
targets,
collation,
outgoing,
);
let exit = inner_exit_2.clone();
tokio::spawn(future::select(res.boxed(), exit).map(drop).map(|_| Ok(())).compat());
})
});
future::Either::Right(collation_work)
});
let deadlined = future::select(
work,
futures_timer::Delay::new(COLLATION_TIMEOUT)
);
let silenced = deadlined
.map(|either| {
if let future::Either::Right(_) = either {
warn!("Collation failure: timeout");
}
});
let future = future::select(
silenced,
inner_exit.clone()
).map(drop);
tokio::spawn(future);
future::ready(())
});
tokio::spawn(future.map(|_| Ok(())).compat());
future::ready(())
});
let work_and_exit = future::select(work, exit)
.map(|_| ());
service.spawn_essential_task(work.map(|_| Ok::<_, ()>(())).compat());
Box::new(work_and_exit)
}
polkadot_cli::run_until_exit(runtime, service, exit)
}
fn compute_targets(para_id: ParaId, session_keys: &[ValidatorId], roster: DutyRoster) -> HashSet<ValidatorId> {
@@ -477,6 +450,11 @@ fn compute_targets(para_id: ParaId, session_keys: &[ValidatorId], roster: DutyRo
.collect()
}
/// Set the `collating_for` parameter of the configuration.
fn prepare_config(config: &mut Configuration, para_id: ParaId, key: &Arc<CollatorPair>) {
config.custom.collating_for = Some((key.public(), para_id));
}
/// Run a collator node with the given `RelayChainContext` and `ParachainContext`
/// build by the given `BuildParachainContext` and arguments to the underlying polkadot node.
///
@@ -487,15 +465,25 @@ pub fn run_collator<P, E>(
para_id: ParaId,
exit: E,
key: Arc<CollatorPair>,
version: VersionInfo,
mut config: Configuration,
) -> polkadot_cli::error::Result<()> where
P: BuildParachainContext + Send + 'static,
P: BuildParachainContext,
P::ParachainContext: Send + 'static,
<P::ParachainContext as ParachainContext>::ProduceCandidate: Send + 'static,
<P::ParachainContext as ParachainContext>::ProduceCandidate: Send,
E: futures::Future<Output = ()> + Unpin + Send + Clone + Sync + 'static,
{
let node_logic = CollationNode { build_parachain_context, exit, para_id, key };
polkadot_cli::run(node_logic, version)
prepare_config(&mut config, para_id, &key);
match (config.is_kusama(), config.roles) {
(true, Roles::LIGHT) =>
run_collator_node(service::kusama_new_light(config)?, exit, para_id, key, build_parachain_context),
(true, _) =>
run_collator_node(service::kusama_new_full(config)?, exit, para_id, key, build_parachain_context),
(false, Roles::LIGHT) =>
run_collator_node(service::polkadot_new_light(config)?, exit, para_id, key, build_parachain_context),
(false, _) =>
run_collator_node(service::polkadot_new_full(config)?, exit, para_id, key, build_parachain_context),
}
}
#[cfg(test)]
@@ -601,4 +589,3 @@ mod tests {
assert_eq!(collation.info.egress_queue_roots, vec![(a, root_a), (b, root_b)]);
}
}