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 e286e0451c
commit 07b3c44be8
21 changed files with 2098 additions and 1198 deletions
+118 -56
View File
@@ -22,13 +22,14 @@ mod chain_spec;
mod genesis;
use core::future::Future;
use cumulus_client_consensus_common::{ParachainCandidate, ParachainConsensus};
use cumulus_client_network::BlockAnnounceValidator;
use cumulus_client_service::{
prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams,
};
use cumulus_primitives_core::ParaId;
use cumulus_test_runtime::{NodeBlock as Block, RuntimeApi};
use polkadot_primitives::v1::CollatorPair;
use cumulus_test_runtime::{Hash, Header, NodeBlock as Block, RuntimeApi};
use polkadot_primitives::v1::{CollatorPair, Hash as PHash, PersistedValidationData};
use sc_client_api::execution_extensions::ExecutionStrategies;
use sc_executor::native_executor_instance;
pub use sc_executor::NativeExecutor;
@@ -45,11 +46,7 @@ use sp_arithmetic::traits::SaturatedConversion;
use sp_blockchain::HeaderBackend;
use sp_core::{Pair, H256};
use sp_keyring::Sr25519Keyring;
use sp_runtime::{
codec::Encode,
generic,
traits::BlakeTwo256
};
use sp_runtime::{codec::Encode, generic, traits::BlakeTwo256};
use sp_state_machine::BasicExternalities;
use sp_trie::PrefixedMemoryDB;
use std::sync::Arc;
@@ -62,6 +59,25 @@ pub use cumulus_test_runtime as runtime;
pub use genesis::*;
pub use sp_keyring::Sr25519Keyring as Keyring;
/// A consensus that will never produce any block.
#[derive(Clone)]
struct NullConsensus;
#[async_trait::async_trait]
impl ParachainConsensus<Block> for NullConsensus {
async fn produce_candidate(
&mut self,
_: &Header,
_: PHash,
_: &PersistedValidationData,
) -> Option<ParachainCandidate<Block>> {
None
}
}
/// The signature of the announce block fn.
pub type AnnounceBlockFn = Arc<dyn Fn(Hash, Option<Vec<u8>>) + Send + Sync>;
// Native executor instance.
native_executor_instance!(
pub RuntimeExecutor,
@@ -134,7 +150,9 @@ async fn start_node_impl<RB>(
collator_key: Option<CollatorPair>,
relay_chain_config: Configuration,
para_id: ParaId,
wrap_announce_block: Option<Box<dyn FnOnce(AnnounceBlockFn) -> AnnounceBlockFn>>,
rpc_ext_builder: RB,
consensus: Consensus,
) -> sc_service::error::Result<(
TaskManager,
Arc<TFullClient<Block, RuntimeApi, RuntimeExecutor>>,
@@ -185,14 +203,14 @@ where
let block_announce_validator_builder = move |_| Box::new(block_announce_validator) as Box<_>;
let prometheus_registry = parachain_config.prometheus_registry().cloned();
let import_queue = params.import_queue;
let import_queue = cumulus_client_service::SharedImportQueue::new(params.import_queue);
let (network, network_status_sinks, system_rpc_tx, start_network) =
sc_service::build_network(sc_service::BuildNetworkParams {
config: &parachain_config,
client: client.clone(),
transaction_pool: transaction_pool.clone(),
spawn_handle: task_manager.spawn_handle(),
import_queue,
import_queue: import_queue.clone(),
on_demand: None,
block_announce_validator_builder: Some(Box::new(block_announce_validator_builder)),
})?;
@@ -224,43 +242,57 @@ where
Arc::new(move |hash, data| network.announce_block(hash, data))
};
let announce_block = wrap_announce_block
.map(|w| (w)(announce_block.clone()))
.unwrap_or_else(|| announce_block);
if let Some(collator_key) = collator_key {
let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording(
task_manager.spawn_handle(),
client.clone(),
transaction_pool,
prometheus_registry.as_ref(),
None,
);
let parachain_consensus: Box<dyn ParachainConsensus<Block>> = match consensus {
Consensus::RelayChain => {
let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording(
task_manager.spawn_handle(),
client.clone(),
transaction_pool,
prometheus_registry.as_ref(),
None,
);
let relay_chain_client = relay_chain_full_node.client.clone();
let relay_chain_backend = relay_chain_full_node.backend.clone();
let relay_chain_client = relay_chain_full_node.client.clone();
let relay_chain_backend = relay_chain_full_node.backend.clone();
let parachain_consensus = cumulus_client_consensus_relay_chain::RelayChainConsensus::new(
para_id,
proposer_factory,
move |_, (relay_parent, validation_data)| {
let parachain_inherent =
cumulus_primitives_parachain_inherent::ParachainInherentData::create_at(
relay_parent,
&*relay_chain_client,
&*relay_chain_backend,
&validation_data,
Box::new(
cumulus_client_consensus_relay_chain::RelayChainConsensus::new(
para_id,
);
async move {
let time = sp_timestamp::InherentDataProvider::from_system_time();
proposer_factory,
move |_, (relay_parent, validation_data)| {
let parachain_inherent =
cumulus_primitives_parachain_inherent::ParachainInherentData::create_at(
relay_parent,
&*relay_chain_client,
&*relay_chain_backend,
&validation_data,
para_id,
);
let parachain_inherent = parachain_inherent.ok_or_else(|| {
Box::<dyn std::error::Error + Send + Sync>::from(String::from("error"))
})?;
Ok((time, parachain_inherent))
}
},
client.clone(),
relay_chain_full_node.client.clone(),
relay_chain_full_node.backend.clone(),
);
async move {
let time = sp_timestamp::InherentDataProvider::from_system_time();
let parachain_inherent = parachain_inherent.ok_or_else(|| {
Box::<dyn std::error::Error + Send + Sync>::from(String::from(
"error",
))
})?;
Ok((time, parachain_inherent))
}
},
client.clone(),
relay_chain_full_node.client.clone(),
relay_chain_full_node.backend.clone(),
),
)
}
Consensus::Null => Box::new(NullConsensus),
};
let relay_chain_full_node =
relay_chain_full_node.with_client(polkadot_test_service::TestClient);
@@ -273,8 +305,9 @@ where
task_manager: &mut task_manager,
para_id,
collator_key,
parachain_consensus: Box::new(parachain_consensus),
parachain_consensus,
relay_chain_full_node,
import_queue,
};
start_collator(params).await?;
@@ -287,7 +320,7 @@ where
announce_block,
task_manager: &mut task_manager,
para_id,
polkadot_full_node: relay_chain_full_node,
relay_chain_full_node,
};
start_full_node(params)?;
@@ -313,6 +346,12 @@ pub struct TestNode {
pub rpc_handlers: RpcHandlers,
}
enum Consensus {
/// Use the relay-chain provided consensus.
RelayChain,
/// Use the null consensus that will never produce any block.
Null,
}
/// A builder to create a [`TestNode`].
pub struct TestNodeBuilder {
@@ -323,8 +362,10 @@ pub struct TestNodeBuilder {
parachain_nodes: Vec<MultiaddrWithPeerId>,
parachain_nodes_exclusive: bool,
relay_chain_nodes: Vec<MultiaddrWithPeerId>,
wrap_announce_block: Option<Box<dyn FnOnce(AnnounceBlockFn) -> AnnounceBlockFn>>,
storage_update_func_parachain: Option<Box<dyn Fn()>>,
storage_update_func_relay_chain: Option<Box<dyn Fn()>>,
consensus: Consensus,
}
impl TestNodeBuilder {
@@ -342,8 +383,10 @@ impl TestNodeBuilder {
parachain_nodes: Vec::new(),
parachain_nodes_exclusive: false,
relay_chain_nodes: Vec::new(),
wrap_announce_block: None,
storage_update_func_parachain: None,
storage_update_func_relay_chain: None,
consensus: Consensus::RelayChain,
}
}
@@ -404,32 +447,43 @@ impl TestNodeBuilder {
mut self,
nodes: impl IntoIterator<Item = &'a polkadot_test_service::PolkadotTestNode>,
) -> Self {
self.relay_chain_nodes.extend(nodes.into_iter().map(|n| n.addr.clone()));
self.relay_chain_nodes
.extend(nodes.into_iter().map(|n| n.addr.clone()));
self
}
/// Wrap the announce block function of this node.
pub fn wrap_announce_block(
mut self,
wrap: impl FnOnce(AnnounceBlockFn) -> AnnounceBlockFn + 'static,
) -> Self {
self.wrap_announce_block = Some(Box::new(wrap));
self
}
/// Allows accessing the parachain storage before the test node is built.
pub fn update_storage_parachain(
mut self,
updater: impl Fn() + 'static,
) -> Self {
pub fn update_storage_parachain(mut self, updater: impl Fn() + 'static) -> Self {
self.storage_update_func_parachain = Some(Box::new(updater));
self
}
/// Allows accessing the relay chain storage before the test node is built.
pub fn update_storage_relay_chain(
mut self,
updater: impl Fn() + 'static,
) -> Self {
pub fn update_storage_relay_chain(mut self, updater: impl Fn() + 'static) -> Self {
self.storage_update_func_relay_chain = Some(Box::new(updater));
self
}
/// Use the null consensus that will never author any block.
pub fn use_null_consensus(mut self) -> Self {
self.consensus = Consensus::Null;
self
}
/// Build the [`TestNode`].
pub async fn build(self) -> TestNode {
let parachain_config = node_config(
self.storage_update_func_parachain.unwrap_or_else(|| Box::new(|| ())),
self.storage_update_func_parachain
.unwrap_or_else(|| Box::new(|| ())),
self.task_executor.clone(),
self.key.clone(),
self.parachain_nodes,
@@ -439,7 +493,8 @@ impl TestNodeBuilder {
)
.expect("could not generate Configuration");
let mut relay_chain_config = polkadot_test_service::node_config(
self.storage_update_func_relay_chain.unwrap_or_else(|| Box::new(|| ())),
self.storage_update_func_relay_chain
.unwrap_or_else(|| Box::new(|| ())),
self.task_executor,
self.key,
self.relay_chain_nodes,
@@ -455,7 +510,9 @@ impl TestNodeBuilder {
self.collator_key,
relay_chain_config,
self.para_id,
self.wrap_announce_block,
|_| Default::default(),
self.consensus,
)
.await
.expect("could not create Cumulus test service");
@@ -611,7 +668,9 @@ impl TestNode {
self.send_extrinsic(
runtime::SudoCall::sudo_unchecked_weight(Box::new(call.into()), 1_000),
Sr25519Keyring::Alice,
).await.map(drop)
)
.await
.map(drop)
}
}
@@ -634,7 +693,10 @@ pub fn construct_extrinsic(
let extra: runtime::SignedExtra = (
frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
frame_system::CheckGenesis::<runtime::Runtime>::new(),
frame_system::CheckEra::<runtime::Runtime>::from(generic::Era::mortal(period, current_block)),
frame_system::CheckEra::<runtime::Runtime>::from(generic::Era::mortal(
period,
current_block,
)),
frame_system::CheckNonce::<runtime::Runtime>::from(nonce),
frame_system::CheckWeight::<runtime::Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(tip),