Pov recovery for parachains (#445)

* Start with a failing integration test & some refactorings

* More work

* Make it "work"

* Add `NullConsensus` for the test

* More refactorings

* Move stuff over to its own crate

* Refactorings

* Integrate it into `service` and make the test working

* Docs and some exit condition

* Use the real import queue

* Fix tests

* Update client/pov-recovery/src/active_candidate_recovery.rs

Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>

* Fetch slot duration from the relay chain

* Docs

* Fixes

Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>
This commit is contained in:
Bastian Köcher
2021-05-26 15:02:42 +02:00
committed by GitHub
parent 36c6da180a
commit 7ffb205b0d
21 changed files with 2098 additions and 1198 deletions
+118 -26
View File
@@ -20,19 +20,25 @@
use cumulus_client_consensus_common::ParachainConsensus;
use cumulus_primitives_core::{CollectCollationInfo, ParaId};
use futures::FutureExt;
use polkadot_overseer::OverseerHandler;
use polkadot_primitives::v1::{Block as PBlock, CollatorPair};
use polkadot_service::{AbstractClient, Client as PClient, ClientHandle, RuntimeApiCollection};
use sc_client_api::{
Backend as BackendT, BlockBackend, BlockchainEvents, Finalizer, UsageProvider,
};
use sc_service::{error::Result as ServiceResult, Configuration, Role, TaskManager};
use sc_service::{Configuration, Role, TaskManager};
use sc_telemetry::TelemetryWorkerHandle;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_consensus::BlockImport;
use sp_consensus::{
import_queue::{ImportQueue, IncomingBlock, Link, Origin},
BlockImport, BlockOrigin,
};
use sp_core::traits::SpawnNamed;
use sp_runtime::traits::{BlakeTwo256, Block as BlockT};
use sp_runtime::{
traits::{BlakeTwo256, Block as BlockT, NumberFor},
Justifications,
};
use std::{marker::PhantomData, sync::Arc};
pub mod genesis;
@@ -41,7 +47,7 @@ pub mod genesis;
type RFullNode<C> = polkadot_service::NewFull<C>;
/// Parameters given to [`start_collator`].
pub struct StartCollatorParams<'a, Block: BlockT, BS, Client, Spawner, RClient> {
pub struct StartCollatorParams<'a, Block: BlockT, BS, Client, Spawner, RClient, IQ> {
pub block_status: Arc<BS>,
pub client: Arc<Client>,
pub announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
@@ -51,6 +57,7 @@ pub struct StartCollatorParams<'a, Block: BlockT, BS, Client, Spawner, RClient>
pub relay_chain_full_node: RFullNode<RClient>,
pub task_manager: &'a mut TaskManager,
pub parachain_consensus: Box<dyn ParachainConsensus<Block>>,
pub import_queue: IQ,
}
/// Start a collator node for a parachain.
@@ -58,7 +65,7 @@ pub struct StartCollatorParams<'a, Block: BlockT, BS, Client, Spawner, RClient>
/// A collator is similar to a validator in a normal blockchain.
/// It is responsible for producing blocks and sending the blocks to a
/// parachain validator for validation and inclusion into the relay chain.
pub async fn start_collator<'a, Block, BS, Client, Backend, Spawner, RClient>(
pub async fn start_collator<'a, Block, BS, Client, Backend, Spawner, RClient, IQ>(
StartCollatorParams {
block_status,
client,
@@ -69,7 +76,8 @@ pub async fn start_collator<'a, Block, BS, Client, Backend, Spawner, RClient>(
task_manager,
relay_chain_full_node,
parachain_consensus,
}: StartCollatorParams<'a, Block, BS, Client, Spawner, RClient>,
import_queue,
}: StartCollatorParams<'a, Block, BS, Client, Spawner, RClient, IQ>,
) -> sc_service::error::Result<()>
where
Block: BlockT,
@@ -88,6 +96,7 @@ where
Spawner: SpawnNamed + Clone + Send + Sync + 'static,
RClient: ClientHandle,
Backend: BackendT<Block> + 'static,
IQ: ImportQueue<Block> + 'static,
{
relay_chain_full_node.client.execute_with(StartConsensus {
para_id,
@@ -95,6 +104,18 @@ where
client: client.clone(),
task_manager,
_phantom: PhantomData,
});
relay_chain_full_node.client.execute_with(StartPoVRecovery {
para_id,
client: client.clone(),
import_queue,
task_manager,
overseer_handler: relay_chain_full_node
.overseer_handler
.clone()
.ok_or_else(|| "Polkadot full node did not provided an `OverseerHandler`!")?,
_phantom: PhantomData,
})?;
cumulus_client_collator::start_collator(cumulus_client_collator::StartCollatorParams {
@@ -120,7 +141,7 @@ where
pub struct StartFullNodeParams<'a, Block: BlockT, Client, PClient> {
pub para_id: ParaId,
pub client: Arc<Client>,
pub polkadot_full_node: RFullNode<PClient>,
pub relay_chain_full_node: RFullNode<PClient>,
pub task_manager: &'a mut TaskManager,
pub announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
}
@@ -134,7 +155,7 @@ pub fn start_full_node<Block, Client, Backend, PClient>(
client,
announce_block,
task_manager,
polkadot_full_node,
relay_chain_full_node,
para_id,
}: StartFullNodeParams<Block, Client, PClient>,
) -> sc_service::error::Result<()>
@@ -151,15 +172,15 @@ where
Backend: BackendT<Block> + 'static,
PClient: ClientHandle,
{
polkadot_full_node.client.execute_with(StartConsensus {
relay_chain_full_node.client.execute_with(StartConsensus {
announce_block,
para_id,
client,
task_manager,
_phantom: PhantomData,
})?;
});
task_manager.add_child(polkadot_full_node.task_manager);
task_manager.add_child(relay_chain_full_node.task_manager);
Ok(())
}
@@ -186,7 +207,7 @@ where
for<'b> &'b Client: BlockImport<Block>,
Backend: BackendT<Block> + 'static,
{
type Output = ServiceResult<()>;
type Output = ();
fn execute_with_client<PClient, Api, PBackend>(self, client: Arc<PClient>) -> Self::Output
where
@@ -198,24 +219,60 @@ where
{
let consensus = cumulus_client_consensus_common::run_parachain_consensus(
self.para_id,
self.client,
client,
self.client.clone(),
client.clone(),
self.announce_block,
);
self.task_manager.spawn_essential_handle().spawn(
"cumulus-consensus",
consensus.then(|r| async move {
if let Err(e) = r {
tracing::error!(
target: "cumulus-service",
error = %e,
"Parachain consensus failed.",
)
}
}),
self.task_manager
.spawn_essential_handle()
.spawn("cumulus-consensus", consensus);
}
}
struct StartPoVRecovery<'a, Block: BlockT, Client, IQ> {
para_id: ParaId,
client: Arc<Client>,
task_manager: &'a mut TaskManager,
overseer_handler: OverseerHandler,
import_queue: IQ,
_phantom: PhantomData<Block>,
}
impl<'a, Block, Client, IQ> polkadot_service::ExecuteWithClient for StartPoVRecovery<'a, Block, Client, IQ>
where
Block: BlockT,
Client: UsageProvider<Block>
+ Send
+ Sync
+ BlockBackend<Block>
+ BlockchainEvents<Block>
+ 'static,
IQ: ImportQueue<Block> + 'static,
{
type Output = sc_service::error::Result<()>;
fn execute_with_client<PClient, Api, PBackend>(self, client: Arc<PClient>) -> Self::Output
where
<Api as sp_api::ApiExt<PBlock>>::StateBackend: sp_api::StateBackend<BlakeTwo256>,
PBackend: sc_client_api::Backend<PBlock>,
PBackend::State: sp_api::StateBackend<BlakeTwo256>,
Api: RuntimeApiCollection<StateBackend = PBackend::State>,
PClient: AbstractClient<PBlock, PBackend, Api = Api> + 'static,
{
let pov_recovery = cumulus_client_pov_recovery::PoVRecovery::new(
self.overseer_handler,
sc_consensus_babe::Config::get_or_compute(&*client)?.slot_duration(),
self.client,
self.import_queue,
client,
self.para_id,
);
self.task_manager
.spawn_essential_handle()
.spawn("cumulus-pov-recovery", pov_recovery.run());
Ok(())
}
}
@@ -253,3 +310,38 @@ pub fn build_polkadot_full_node(
)
}
}
/// A shared import queue
///
/// This is basically a hack until the Substrate side is implemented properly.
#[derive(Clone)]
pub struct SharedImportQueue<Block: BlockT>(Arc<parking_lot::Mutex<dyn ImportQueue<Block>>>);
impl<Block: BlockT> SharedImportQueue<Block> {
/// Create a new instance of the shared import queue.
pub fn new<IQ: ImportQueue<Block> + 'static>(import_queue: IQ) -> Self {
Self(Arc::new(parking_lot::Mutex::new(import_queue)))
}
}
impl<Block: BlockT> ImportQueue<Block> for SharedImportQueue<Block> {
fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec<IncomingBlock<Block>>) {
self.0.lock().import_blocks(origin, blocks)
}
fn import_justifications(
&mut self,
who: Origin,
hash: Block::Hash,
number: NumberFor<Block>,
justifications: Justifications,
) {
self.0
.lock()
.import_justifications(who, hash, number, justifications)
}
fn poll_actions(&mut self, cx: &mut std::task::Context, link: &mut dyn Link<Block>) {
self.0.lock().poll_actions(cx, link)
}
}