From ae89dcc325d23901884c90edd062b727f79c5e2b Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 17:26:54 +0100 Subject: [PATCH 01/22] remove deprecated cumulus collator code --- cumulus/client/collator/src/lib.rs | 174 +---------------------------- cumulus/client/service/src/lib.rs | 134 +--------------------- cumulus/test/service/src/lib.rs | 155 +++++++++++-------------- 3 files changed, 69 insertions(+), 394 deletions(-) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 9cd08bb06c3ae..81d8eee023a03 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -17,128 +17,18 @@ //! Cumulus Collator implementation for Substrate. -use cumulus_primitives_core::{ - relay_chain::Hash as PHash, CollectCollationInfo, PersistedValidationData, -}; - -use sc_client_api::BlockBackend; -use sp_api::ProvideRuntimeApi; -use sp_core::traits::SpawnNamed; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +use sp_runtime::traits::Block as BlockT; use cumulus_client_consensus_common::ParachainConsensus; -use polkadot_node_primitives::{CollationGenerationConfig, CollationResult, MaybeCompressedPoV}; +use polkadot_node_primitives::CollationGenerationConfig; use polkadot_node_subsystem::messages::{CollationGenerationMessage, CollatorProtocolMessage}; use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{CollatorPair, Id as ParaId}; -use codec::Decode; -use futures::prelude::*; use std::sync::Arc; -use crate::service::CollatorService; - pub mod service; -/// The logging target. -const LOG_TARGET: &str = "cumulus-collator"; - -/// The implementation of the Cumulus `Collator`. -/// -/// Note that this implementation is soon to be deprecated and removed, and it is suggested to -/// directly use the [`CollatorService`] instead, so consensus engine implementations -/// live at the top level. -pub struct Collator { - service: CollatorService, - parachain_consensus: Box>, -} - -impl Clone for Collator { - fn clone(&self) -> Self { - Collator { - service: self.service.clone(), - parachain_consensus: self.parachain_consensus.clone(), - } - } -} - -impl Collator -where - Block: BlockT, - BS: BlockBackend, - RA: ProvideRuntimeApi, - RA::Api: CollectCollationInfo, -{ - /// Create a new instance. - fn new( - collator_service: CollatorService, - parachain_consensus: Box>, - ) -> Self { - Self { service: collator_service, parachain_consensus } - } - - async fn produce_candidate( - mut self, - relay_parent: PHash, - validation_data: PersistedValidationData, - ) -> Option { - tracing::trace!( - target: LOG_TARGET, - relay_parent = ?relay_parent, - "Producing candidate", - ); - - let last_head = match Block::Header::decode(&mut &validation_data.parent_head.0[..]) { - Ok(x) => x, - Err(e) => { - tracing::error!( - target: LOG_TARGET, - error = ?e, - "Could not decode the head data." - ); - return None - }, - }; - - let last_head_hash = last_head.hash(); - if !self.service.check_block_status(last_head_hash, &last_head) { - return None - } - - tracing::info!( - target: LOG_TARGET, - relay_parent = ?relay_parent, - at = ?last_head_hash, - "Starting collation.", - ); - - let candidate = self - .parachain_consensus - .produce_candidate(&last_head, relay_parent, &validation_data) - .await?; - - let block_hash = candidate.block.header().hash(); - - let (collation, b) = self.service.build_collation(&last_head, block_hash, candidate)?; - - b.log_size_info(); - - if let MaybeCompressedPoV::Compressed(ref pov) = collation.proof_of_validity { - tracing::info!( - target: LOG_TARGET, - "Compressed PoV size: {}kb", - pov.block_data.0.len() as f64 / 1024f64, - ); - } - - let result_sender = self.service.announce_with_barrier(block_hash); - - tracing::info!(target: LOG_TARGET, ?block_hash, "Produced proof-of-validity candidate.",); - - Some(CollationResult { collation, result_sender: Some(result_sender) }) - } -} - /// Relay-chain-driven collators are those whose block production is driven purely /// by new relay chain blocks and the most recently included parachain blocks /// within them. @@ -267,66 +157,6 @@ pub struct StartCollatorParams { pub key: CollatorPair, pub parachain_consensus: Box>, } - -/// Start the collator. -#[deprecated = "Collators should run consensus futures which handle this logic internally"] -pub async fn start_collator( - params: StartCollatorParams, -) where - Block: BlockT, - BS: BlockBackend + Send + Sync + 'static, - Spawner: SpawnNamed + Clone + Send + Sync + 'static, - RA: ProvideRuntimeApi + Send + Sync + 'static, - RA::Api: CollectCollationInfo, -{ - // This never needed to be asynchronous, but shouldn't be changed due to backcompat. - #[allow(deprecated)] - start_collator_sync(params); -} - -/// Start the collator in a synchronous function. -#[deprecated = "Collators should run consensus futures which handle this logic internally"] -pub fn start_collator_sync( - StartCollatorParams { - para_id, - block_status, - announce_block, - overseer_handle, - spawner, - key, - parachain_consensus, - runtime_api, - }: StartCollatorParams, -) where - Block: BlockT, - BS: BlockBackend + Send + Sync + 'static, - Spawner: SpawnNamed + Clone + Send + Sync + 'static, - RA: ProvideRuntimeApi + Send + Sync + 'static, - RA::Api: CollectCollationInfo, -{ - let collator_service = - CollatorService::new(block_status, Arc::new(spawner.clone()), announce_block, runtime_api); - - let collator = Collator::new(collator_service, parachain_consensus); - - let collation_future = Box::pin(async move { - let mut request_stream = relay_chain_driven::init(key, para_id, overseer_handle).await; - while let Some(request) = request_stream.next().await { - let collation = collator - .clone() - .produce_candidate( - *request.relay_parent(), - request.persisted_validation_data().clone(), - ) - .await; - - request.complete(collation); - } - }); - - spawner.spawn("cumulus-relay-driven-collator", None, collation_future); -} - #[cfg(test)] mod tests { use super::*; diff --git a/cumulus/client/service/src/lib.rs b/cumulus/client/service/src/lib.rs index c00b7dd6f401b..86593fd09ae8c 100644 --- a/cumulus/client/service/src/lib.rs +++ b/cumulus/client/service/src/lib.rs @@ -49,7 +49,7 @@ use sc_telemetry::{log, TelemetryWorkerHandle}; use sc_utils::mpsc::TracingUnboundedSender; use sp_api::ProvideRuntimeApi; use sp_blockchain::{HeaderBackend, HeaderMetadata}; -use sp_core::{traits::SpawnNamed, Decode}; +use sp_core::Decode; use sp_runtime::{ traits::{Block as BlockT, BlockIdTo, Header}, SaturatedConversion, Saturating, @@ -132,82 +132,6 @@ pub struct StartFullNodeParams<'a, Block: BlockT, Client, RCInterface> { pub prometheus_registry: Option<&'a Registry>, } -/// Start a collator node for a parachain. -/// -/// 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. -#[deprecated = "use start_relay_chain_tasks instead"] -pub async fn start_collator<'a, Block, BS, Client, Backend, RCInterface, Spawner>( - StartCollatorParams { - block_status, - client, - announce_block, - spawner, - para_id, - task_manager, - relay_chain_interface, - parachain_consensus, - import_queue, - collator_key, - relay_chain_slot_duration, - recovery_handle, - sync_service, - prometheus_registry, - }: StartCollatorParams<'a, Block, BS, Client, RCInterface, Spawner>, -) -> sc_service::error::Result<()> -where - Block: BlockT, - BS: BlockBackend + Send + Sync + 'static, - Client: Finalizer - + UsageProvider - + HeaderBackend - + Send - + Sync - + BlockBackend - + BlockchainEvents - + ProvideRuntimeApi - + 'static, - Client::Api: CollectCollationInfo, - for<'b> &'b Client: BlockImport, - Spawner: SpawnNamed + Clone + Send + Sync + 'static, - RCInterface: RelayChainInterface + Clone + 'static, - Backend: BackendT + 'static, -{ - let overseer_handle = relay_chain_interface - .overseer_handle() - .map_err(|e| sc_service::Error::Application(Box::new(e)))?; - - start_relay_chain_tasks(StartRelayChainTasksParams { - client: client.clone(), - announce_block: announce_block.clone(), - para_id, - task_manager, - da_recovery_profile: DARecoveryProfile::Collator, - relay_chain_interface, - import_queue, - relay_chain_slot_duration, - recovery_handle, - sync_service, - prometheus_registry, - })?; - - #[allow(deprecated)] - cumulus_client_collator::start_collator(cumulus_client_collator::StartCollatorParams { - runtime_api: client, - block_status, - announce_block, - overseer_handle, - spawner, - para_id, - key: collator_key, - parachain_consensus, - }) - .await; - - Ok(()) -} - /// Start necessary consensus tasks related to the relay chain. /// /// Parachain nodes need to track the state of the relay chain and use the @@ -312,62 +236,6 @@ where Ok(()) } -/// Start a full node for a parachain. -/// -/// A full node will only sync the given parachain and will follow the -/// tip of the chain. -#[deprecated = "use start_relay_chain_tasks instead"] -pub fn start_full_node( - StartFullNodeParams { - client, - announce_block, - task_manager, - relay_chain_interface, - para_id, - relay_chain_slot_duration, - import_queue, - recovery_handle, - sync_service, - prometheus_registry, - }: StartFullNodeParams, -) -> sc_service::error::Result<()> -where - Block: BlockT, - Client: Finalizer - + UsageProvider - + HeaderBackend - + Send - + Sync - + BlockBackend - + BlockchainEvents - + 'static, - for<'a> &'a Client: BlockImport, - Backend: BackendT + 'static, - RCInterface: RelayChainInterface + Clone + 'static, -{ - start_relay_chain_tasks(StartRelayChainTasksParams { - client, - announce_block, - task_manager, - relay_chain_interface, - para_id, - relay_chain_slot_duration, - import_queue, - recovery_handle, - sync_service, - da_recovery_profile: DARecoveryProfile::FullNode, - prometheus_registry, - }) -} - -/// Re-exports of old parachain consensus loop start logic. -#[deprecated = "This is old consensus architecture only for backwards compatibility \ - and will be removed in the future"] -pub mod old_consensus { - #[allow(deprecated)] - pub use cumulus_client_collator::{start_collator, start_collator_sync, StartCollatorParams}; -} - /// Prepare the parachain's node configuration /// /// This function will: diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index 48a4cc76ca6f2..28fac92039246 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -52,8 +52,6 @@ use cumulus_client_consensus_common::{ ParachainBlockImport as TParachainBlockImport, ParachainCandidate, ParachainConsensus, }; use cumulus_client_pov_recovery::{RecoveryDelayRange, RecoveryHandle}; -#[allow(deprecated)] -use cumulus_client_service::old_consensus; use cumulus_client_service::{ build_network, prepare_node_config, start_relay_chain_tasks, BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams, @@ -454,96 +452,75 @@ where })?; if let Some(collator_key) = collator_key { - if let Consensus::Null = consensus { - #[allow(deprecated)] - old_consensus::start_collator(old_consensus::StartCollatorParams { - block_status: client.clone(), - announce_block, - runtime_api: client.clone(), + let proposer = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool.clone(), + prometheus_registry.as_ref(), + None, + ); + + let collator_service = CollatorService::new( + client.clone(), + Arc::new(task_manager.spawn_handle()), + announce_block, + client.clone(), + ); + + let client_for_aura = client.clone(); + + if use_slot_based_collator { + tracing::info!(target: LOG_TARGET, "Starting block authoring with slot based authoring."); + let params = SlotBasedParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend.clone(), + relay_client: relay_chain_interface, + code_hash_provider: move |block_hash| { + client_for_aura.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) + }, + keystore, + collator_key, + relay_chain_slot_duration, + para_id, + proposer, + collator_service, + authoring_duration: Duration::from_millis(2000), + reinitialize: false, + slot_offset: Duration::from_secs(1), + block_import_handle: slot_based_handle, spawner: task_manager.spawn_handle(), + export_pov: None, + max_pov_percentage: None, + }; + + slot_based::run::(params); + } else { + tracing::info!(target: LOG_TARGET, "Starting block authoring with lookahead collator."); + let params = AuraParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend.clone(), + relay_client: relay_chain_interface, + code_hash_provider: move |block_hash| { + client_for_aura.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) + }, + keystore, + collator_key, para_id, - parachain_consensus: Box::new(NullConsensus) as Box<_>, - key: collator_key, overseer_handle, - }) - .await; - } else { - let proposer = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool.clone(), - prometheus_registry.as_ref(), - None, - ); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let client_for_aura = client.clone(); - - if use_slot_based_collator { - tracing::info!(target: LOG_TARGET, "Starting block authoring with slot based authoring."); - let params = SlotBasedParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend.clone(), - relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client_for_aura - .code_at(block_hash) - .ok() - .map(|c| ValidationCode::from(c).hash()) - }, - keystore, - collator_key, - relay_chain_slot_duration, - para_id, - proposer, - collator_service, - authoring_duration: Duration::from_millis(2000), - reinitialize: false, - slot_offset: Duration::from_secs(1), - block_import_handle: slot_based_handle, - spawner: task_manager.spawn_handle(), - export_pov: None, - max_pov_percentage: None, - }; - - slot_based::run::(params); - } else { - tracing::info!(target: LOG_TARGET, "Starting block authoring with lookahead collator."); - let params = AuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend.clone(), - relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client_for_aura - .code_at(block_hash) - .ok() - .map(|c| ValidationCode::from(c).hash()) - }, - keystore, - collator_key, - para_id, - overseer_handle, - relay_chain_slot_duration, - proposer, - collator_service, - authoring_duration: Duration::from_millis(2000), - reinitialize: false, - max_pov_percentage: None, - }; - - let fut = aura::run::(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); - } + relay_chain_slot_duration, + proposer, + collator_service, + authoring_duration: Duration::from_millis(2000), + reinitialize: false, + max_pov_percentage: None, + }; + + let fut = aura::run::(params); + task_manager.spawn_essential_handle().spawn("aura", None, fut); } } From b89c290fd07ced9bdfe5c55da83f263296d97246 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 17:42:17 +0100 Subject: [PATCH 02/22] add prdoc --- prdoc/pr_9662.prdoc | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 prdoc/pr_9662.prdoc diff --git a/prdoc/pr_9662.prdoc b/prdoc/pr_9662.prdoc new file mode 100644 index 0000000000000..e41caddc1373b --- /dev/null +++ b/prdoc/pr_9662.prdoc @@ -0,0 +1,9 @@ +title: Removes deprecated collator-related code in cumulus +doc: +- audience: Client Dev + description: Removes deprecated collator-related code in cumulus. +crates: +- name: cumulus-client-collator + bump: major +- name: cumulus-client-service + bump: major \ No newline at end of file From 25b2b7a1b5fd8a686ac5580950b6a64421a1b0ad Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 19:04:05 +0100 Subject: [PATCH 03/22] fix prdoc --- prdoc/pr_9662.prdoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prdoc/pr_9662.prdoc b/prdoc/pr_9662.prdoc index e41caddc1373b..4c05ff3806293 100644 --- a/prdoc/pr_9662.prdoc +++ b/prdoc/pr_9662.prdoc @@ -1,9 +1,11 @@ title: Removes deprecated collator-related code in cumulus doc: -- audience: Client Dev +- audience: Node Dev description: Removes deprecated collator-related code in cumulus. crates: - name: cumulus-client-collator bump: major - name: cumulus-client-service + bump: major +- name: cumulus-test-service bump: major \ No newline at end of file From 5db0f3aaaaace8cf1d4c507d6ba9e98105e779e5 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 19:31:35 +0100 Subject: [PATCH 04/22] remove unused code --- cumulus/test/service/src/cli.rs | 3 -- cumulus/test/service/src/lib.rs | 48 +++----------------------------- cumulus/test/service/src/main.rs | 9 ------ 3 files changed, 4 insertions(+), 56 deletions(-) diff --git a/cumulus/test/service/src/cli.rs b/cumulus/test/service/src/cli.rs index aa719c0593bbc..8699d89693d39 100644 --- a/cumulus/test/service/src/cli.rs +++ b/cumulus/test/service/src/cli.rs @@ -45,9 +45,6 @@ pub struct TestCollatorCli { #[arg(raw = true)] pub relaychain_args: Vec, - #[arg(long)] - pub use_null_consensus: bool, - #[arg(long)] pub disable_block_announcements: bool, diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index 0c362dc59d3a4..d0d2325c21b6c 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -48,9 +48,7 @@ use url::Url; use crate::runtime::Weight; use cumulus_client_cli::{CollatorOptions, RelayChainMode}; -use cumulus_client_consensus_common::{ - ParachainBlockImport as TParachainBlockImport, ParachainCandidate, ParachainConsensus, -}; +use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport; use cumulus_client_pov_recovery::{RecoveryDelayRange, RecoveryHandle}; use cumulus_client_service::{ build_network, prepare_node_config, start_relay_chain_tasks, BuildNetworkParams, @@ -61,12 +59,12 @@ use cumulus_relay_chain_inprocess_interface::RelayChainInProcessInterface; use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult}; use cumulus_relay_chain_minimal_node::build_minimal_relay_chain_node_with_rpc; -use cumulus_test_runtime::{Hash, Header, NodeBlock as Block, RuntimeApi}; +use cumulus_test_runtime::{Hash, NodeBlock as Block, RuntimeApi}; use frame_system_rpc_runtime_api::AccountNonceApi; use polkadot_node_subsystem::{errors::RecoveryError, messages::AvailabilityRecoveryMessage}; use polkadot_overseer::Handle as OverseerHandle; -use polkadot_primitives::{CandidateHash, CollatorPair, Hash as PHash, PersistedValidationData}; +use polkadot_primitives::{CandidateHash, CollatorPair}; use polkadot_service::ProvideRuntimeApi; use sc_consensus::ImportQueue; use sc_network::{ @@ -101,22 +99,6 @@ pub use sp_keyring::Sr25519Keyring as Keyring; const LOG_TARGET: &str = "cumulus-test-service"; -/// A consensus that will never produce any block. -#[derive(Clone)] -struct NullConsensus; - -#[async_trait::async_trait] -impl ParachainConsensus for NullConsensus { - async fn produce_candidate( - &mut self, - _: &Header, - _: PHash, - _: &PersistedValidationData, - ) -> Option> { - None - } -} - /// The signature of the announce block fn. pub type AnnounceBlockFn = Arc>) + Send + Sync>; @@ -321,7 +303,6 @@ pub async fn start_node_impl>( wrap_announce_block: Option AnnounceBlockFn>>, fail_pov_recovery: bool, rpc_ext_builder: RB, - consensus: Consensus, collator_options: CollatorOptions, proof_recording_during_import: bool, use_slot_based_collator: bool, @@ -385,10 +366,7 @@ where metrics: Net::register_notification_metrics( parachain_config.prometheus_config.as_ref().map(|config| &config.registry), ), - sybil_resistance_level: CollatorSybilResistance::Resistant, /* Either Aura that is - * resistant or null that - * is not producing any - * blocks at all. */ + sybil_resistance_level: CollatorSybilResistance::Resistant, }) .await?; @@ -546,14 +524,6 @@ pub struct TestNode { pub backend: Arc, } -#[allow(missing_docs)] -pub enum Consensus { - /// Use Aura consensus. - Aura, - /// Use the null consensus that will never produce any block. - Null, -} - /// A builder to create a [`TestNode`]. pub struct TestNodeBuilder { para_id: ParaId, @@ -566,7 +536,6 @@ pub struct TestNodeBuilder { wrap_announce_block: Option AnnounceBlockFn>>, storage_update_func_parachain: Option>, storage_update_func_relay_chain: Option>, - consensus: Consensus, relay_chain_mode: RelayChainMode, endowed_accounts: Vec, record_proof_during_import: bool, @@ -591,7 +560,6 @@ impl TestNodeBuilder { wrap_announce_block: None, storage_update_func_parachain: None, storage_update_func_relay_chain: None, - consensus: Consensus::Aura, endowed_accounts: Default::default(), relay_chain_mode: RelayChainMode::Embedded, record_proof_during_import: true, @@ -680,12 +648,6 @@ impl TestNodeBuilder { self } - /// Use the null consensus that will never author any block. - pub fn use_null_consensus(mut self) -> Self { - self.consensus = Consensus::Null; - self - } - /// Connect to full node via RPC. pub fn use_external_relay_chain_node_at_url(mut self, network_address: Url) -> Self { self.relay_chain_mode = RelayChainMode::ExternalRpc(vec![network_address]); @@ -754,7 +716,6 @@ impl TestNodeBuilder { self.wrap_announce_block, false, |_| Ok(jsonrpsee::RpcModule::new(())), - self.consensus, collator_options, self.record_proof_during_import, false, @@ -769,7 +730,6 @@ impl TestNodeBuilder { self.wrap_announce_block, false, |_| Ok(jsonrpsee::RpcModule::new(())), - self.consensus, collator_options, self.record_proof_during_import, false, diff --git a/cumulus/test/service/src/main.rs b/cumulus/test/service/src/main.rs index e092c0713a809..c8fdeb46ca615 100644 --- a/cumulus/test/service/src/main.rs +++ b/cumulus/test/service/src/main.rs @@ -96,13 +96,6 @@ fn main() -> Result<(), sc_cli::Error> { let collator_key = parachain_config.role.is_authority().then(|| CollatorPair::generate().0); - let consensus = cli - .use_null_consensus - .then(|| { - tracing::info!("Using null consensus."); - cumulus_test_service::Consensus::Null - }) - .unwrap_or(cumulus_test_service::Consensus::Aura); let use_slot_based_collator = cli.authoring == AuthoringPolicy::SlotBased; let (mut task_manager, _, _, _, _, _) = tokio_runtime .block_on(async move { @@ -118,7 +111,6 @@ fn main() -> Result<(), sc_cli::Error> { cli.disable_block_announcements.then(wrap_announce_block), cli.fail_pov_recovery, |_| Ok(jsonrpsee::RpcModule::new(())), - consensus, collator_options, true, use_slot_based_collator, @@ -135,7 +127,6 @@ fn main() -> Result<(), sc_cli::Error> { cli.disable_block_announcements.then(wrap_announce_block), cli.fail_pov_recovery, |_| Ok(jsonrpsee::RpcModule::new(())), - consensus, collator_options, true, use_slot_based_collator, From aa1bbf29eea87887c60616b11c8b10481e40de1c Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 21:06:06 +0100 Subject: [PATCH 05/22] fix test --- cumulus/client/collator/src/lib.rs | 57 +++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 81d8eee023a03..b6f02b1321c88 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -17,7 +17,10 @@ //! Cumulus Collator implementation for Substrate. -use sp_runtime::traits::Block as BlockT; +use cumulus_primitives_core::{relay_chain::Hash as PHash, PersistedValidationData}; + +use sp_core::traits::SpawnNamed; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use cumulus_client_consensus_common::ParachainConsensus; use polkadot_node_primitives::CollationGenerationConfig; @@ -25,6 +28,8 @@ use polkadot_node_subsystem::messages::{CollationGenerationMessage, CollatorProt use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{CollatorPair, Id as ParaId}; +use codec::Decode; +use futures::prelude::*; use std::sync::Arc; pub mod service; @@ -160,6 +165,7 @@ pub struct StartCollatorParams { #[cfg(test)] mod tests { use super::*; + use crate::service::CollatorService; use async_trait::async_trait; use codec::Encode; use cumulus_client_consensus_common::ParachainCandidate; @@ -171,7 +177,7 @@ mod tests { use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use cumulus_test_runtime::{Block, Header}; use futures::{channel::mpsc, executor::block_on, StreamExt}; - use polkadot_node_primitives::CollationGenerationConfig; + use polkadot_node_primitives::{CollationGenerationConfig, CollationResult}; use polkadot_node_subsystem::messages::CollationGenerationMessage; use polkadot_node_subsystem_test_helpers::ForwardSubsystem; use polkadot_overseer::{dummy::dummy_overseer_builder, HeadSupportsParachains}; @@ -243,18 +249,43 @@ mod tests { spawner.spawn("overseer", None, overseer.run().then(|_| async {}).boxed()); - #[allow(deprecated)] - let collator_start = start_collator(StartCollatorParams { - runtime_api: client.clone(), - block_status: client.clone(), - announce_block: Arc::new(announce_block), - overseer_handle: OverseerHandle::new(handle), - spawner, - para_id, - key: CollatorPair::generate().0, - parachain_consensus: Box::new(DummyParachainConsensus { client }), + let collator_service = CollatorService::new( + client.clone(), + Arc::new(spawner.clone()), + Arc::new(announce_block), + client.clone(), + ); + let mut parachain_consensus = Box::new(DummyParachainConsensus { client }); + + let collation_future = Box::pin(async move { + let mut request_stream = relay_chain_driven::init( + CollatorPair::generate().0, + para_id, + OverseerHandle::new(handle), + ) + .await; + while let Some(request) = request_stream.next().await { + let validation_data = request.persisted_validation_data().clone(); + let last_head = match ::Header::decode( + &mut &validation_data.parent_head.0[..], + ) { + Ok(x) => x, + Err(e) => panic!("Could not decode header: {}", e), + }; + let candidate = parachain_consensus + .produce_candidate(&last_head, *request.relay_parent(), &validation_data) + .await + .expect("candidate to be produced."); + let block_hash = candidate.block.header().hash(); + let (collation, _) = collator_service + .build_collation(&last_head, block_hash, candidate) + .expect("collation to succeed."); + + request.complete(Some(CollationResult { collation, result_sender: None })); + } }); - block_on(collator_start); + + spawner.spawn("cumulus-relay-driven-collator", None, collation_future); let msg = block_on(sub_rx.into_future()) .0 From 20cb1463ff0b8c0c0a9d9a09c4b39f299c8cad26 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 21:09:50 +0100 Subject: [PATCH 06/22] cleanup --- cumulus/client/collator/src/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index b6f02b1321c88..1db8e04bdb88e 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -266,12 +266,9 @@ mod tests { .await; while let Some(request) = request_stream.next().await { let validation_data = request.persisted_validation_data().clone(); - let last_head = match ::Header::decode( - &mut &validation_data.parent_head.0[..], - ) { - Ok(x) => x, - Err(e) => panic!("Could not decode header: {}", e), - }; + let last_head = + ::Header::decode(&mut &validation_data.parent_head.0[..]) + .expect("decode header"); let candidate = parachain_consensus .produce_candidate(&last_head, *request.relay_parent(), &validation_data) .await From a9ec330bf376a2678e206a93a55bc20e6291b36b Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 21:12:11 +0100 Subject: [PATCH 07/22] fix name --- cumulus/client/collator/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 1db8e04bdb88e..94ec298c84553 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -286,7 +286,7 @@ mod tests { let msg = block_on(sub_rx.into_future()) .0 - .expect("message should be send by `start_collator` above."); + .expect("message should be send by `cumulus-relay-driven-collator` above."); let collator_fn = match msg { CollationGenerationMessage::Initialize(CollationGenerationConfig { From 8e27c06e9e55ce3f63277b4ee502566bb0864838 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 21:52:38 +0100 Subject: [PATCH 08/22] remove imports --- cumulus/client/collator/src/lib.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 94ec298c84553..06b03bfc524d0 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -17,10 +17,7 @@ //! Cumulus Collator implementation for Substrate. -use cumulus_primitives_core::{relay_chain::Hash as PHash, PersistedValidationData}; - -use sp_core::traits::SpawnNamed; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +use sp_runtime::traits::Block as BlockT; use cumulus_client_consensus_common::ParachainConsensus; use polkadot_node_primitives::CollationGenerationConfig; @@ -28,8 +25,6 @@ use polkadot_node_subsystem::messages::{CollationGenerationMessage, CollatorProt use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{CollatorPair, Id as ParaId}; -use codec::Decode; -use futures::prelude::*; use std::sync::Arc; pub mod service; From 1112f73c7f171ff9e04d62df470f60d88693203a Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 22:24:01 +0100 Subject: [PATCH 09/22] fix imports --- cumulus/client/collator/src/lib.rs | 32 +++++++++--------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 06b03bfc524d0..328e0bf772f3b 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -17,16 +17,10 @@ //! Cumulus Collator implementation for Substrate. -use sp_runtime::traits::Block as BlockT; - -use cumulus_client_consensus_common::ParachainConsensus; use polkadot_node_primitives::CollationGenerationConfig; use polkadot_node_subsystem::messages::{CollationGenerationMessage, CollatorProtocolMessage}; use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{CollatorPair, Id as ParaId}; - -use std::sync::Arc; - pub mod service; /// Relay-chain-driven collators are those whose block production is driven purely @@ -146,41 +140,33 @@ pub async fn initialize_collator_subsystems( .await; } -/// Parameters for [`start_collator`]. -pub struct StartCollatorParams { - pub para_id: ParaId, - pub runtime_api: Arc, - pub block_status: Arc, - pub announce_block: Arc>) + Send + Sync>, - pub overseer_handle: OverseerHandle, - pub spawner: Spawner, - pub key: CollatorPair, - pub parachain_consensus: Box>, -} #[cfg(test)] mod tests { use super::*; use crate::service::CollatorService; use async_trait::async_trait; - use codec::Encode; - use cumulus_client_consensus_common::ParachainCandidate; - use cumulus_primitives_core::ParachainBlockData; + use codec::{Decode, Encode}; + use cumulus_client_consensus_common::{ParachainCandidate, ParachainConsensus}; + use cumulus_primitives_core::{ + relay_chain::Hash as PHash, ParachainBlockData, PersistedValidationData, + }; use cumulus_test_client::{ Client, ClientBlockImportExt, DefaultTestClientBuilderExt, InitBlockBuilder, TestClientBuilder, TestClientBuilderExt, }; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use cumulus_test_runtime::{Block, Header}; - use futures::{channel::mpsc, executor::block_on, StreamExt}; + use futures::{channel::mpsc, executor::block_on, prelude::*, StreamExt}; use polkadot_node_primitives::{CollationGenerationConfig, CollationResult}; use polkadot_node_subsystem::messages::CollationGenerationMessage; use polkadot_node_subsystem_test_helpers::ForwardSubsystem; use polkadot_overseer::{dummy::dummy_overseer_builder, HeadSupportsParachains}; use polkadot_primitives::HeadData; use sp_consensus::BlockOrigin; - use sp_core::{testing::TaskExecutor, Pair}; - use sp_runtime::traits::BlakeTwo256; + use sp_core::{testing::TaskExecutor, traits::SpawnNamed, Pair}; + use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}; use sp_state_machine::Backend; + use std::sync::Arc; struct AlwaysSupportsParachains; From 8bcc054dbfbb74247a4d4a14d4dde0776ecf548d Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 5 Sep 2025 23:16:45 +0100 Subject: [PATCH 10/22] remove old test --- cumulus/client/collator/src/lib.rs | 172 ----------------------------- 1 file changed, 172 deletions(-) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 328e0bf772f3b..d2a4f34522b34 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -139,175 +139,3 @@ pub async fn initialize_collator_subsystems( .send_msg(CollatorProtocolMessage::CollateOn(para_id), "StartCollator") .await; } - -#[cfg(test)] -mod tests { - use super::*; - use crate::service::CollatorService; - use async_trait::async_trait; - use codec::{Decode, Encode}; - use cumulus_client_consensus_common::{ParachainCandidate, ParachainConsensus}; - use cumulus_primitives_core::{ - relay_chain::Hash as PHash, ParachainBlockData, PersistedValidationData, - }; - use cumulus_test_client::{ - Client, ClientBlockImportExt, DefaultTestClientBuilderExt, InitBlockBuilder, - TestClientBuilder, TestClientBuilderExt, - }; - use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; - use cumulus_test_runtime::{Block, Header}; - use futures::{channel::mpsc, executor::block_on, prelude::*, StreamExt}; - use polkadot_node_primitives::{CollationGenerationConfig, CollationResult}; - use polkadot_node_subsystem::messages::CollationGenerationMessage; - use polkadot_node_subsystem_test_helpers::ForwardSubsystem; - use polkadot_overseer::{dummy::dummy_overseer_builder, HeadSupportsParachains}; - use polkadot_primitives::HeadData; - use sp_consensus::BlockOrigin; - use sp_core::{testing::TaskExecutor, traits::SpawnNamed, Pair}; - use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}; - use sp_state_machine::Backend; - use std::sync::Arc; - - struct AlwaysSupportsParachains; - - #[async_trait] - impl HeadSupportsParachains for AlwaysSupportsParachains { - async fn head_supports_parachains(&self, _head: &PHash) -> bool { - true - } - } - - #[derive(Clone)] - struct DummyParachainConsensus { - client: Arc, - } - - #[async_trait::async_trait] - impl ParachainConsensus for DummyParachainConsensus { - async fn produce_candidate( - &mut self, - parent: &Header, - _: PHash, - validation_data: &PersistedValidationData, - ) -> Option> { - let mut sproof = RelayStateSproofBuilder::default(); - sproof.included_para_head = Some(HeadData(parent.encode())); - sproof.para_id = cumulus_test_runtime::PARACHAIN_ID.into(); - - let cumulus_test_client::BlockBuilderAndSupportData { block_builder, .. } = self - .client - .init_block_builder_at(parent.hash(), Some(validation_data.clone()), sproof); - - let (block, _, proof) = block_builder.build().expect("Creates block").into_inner(); - - self.client - .import(BlockOrigin::Own, block.clone()) - .await - .expect("Imports the block"); - - Some(ParachainCandidate { block, proof: proof.expect("Proof is returned") }) - } - } - - #[test] - fn collates_produces_a_block_and_storage_proof_does_not_contains_code() { - sp_tracing::try_init_simple(); - - let spawner = TaskExecutor::new(); - let para_id = ParaId::from(100); - let announce_block = |_, _| (); - let client = Arc::new(TestClientBuilder::new().build()); - let header = client.header(client.chain_info().genesis_hash).unwrap().unwrap(); - - let (sub_tx, sub_rx) = mpsc::channel(64); - - let (overseer, handle) = - dummy_overseer_builder(spawner.clone(), AlwaysSupportsParachains, None) - .expect("Creates overseer builder") - .replace_collation_generation(|_| ForwardSubsystem(sub_tx)) - .build() - .expect("Builds overseer"); - - spawner.spawn("overseer", None, overseer.run().then(|_| async {}).boxed()); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(spawner.clone()), - Arc::new(announce_block), - client.clone(), - ); - let mut parachain_consensus = Box::new(DummyParachainConsensus { client }); - - let collation_future = Box::pin(async move { - let mut request_stream = relay_chain_driven::init( - CollatorPair::generate().0, - para_id, - OverseerHandle::new(handle), - ) - .await; - while let Some(request) = request_stream.next().await { - let validation_data = request.persisted_validation_data().clone(); - let last_head = - ::Header::decode(&mut &validation_data.parent_head.0[..]) - .expect("decode header"); - let candidate = parachain_consensus - .produce_candidate(&last_head, *request.relay_parent(), &validation_data) - .await - .expect("candidate to be produced."); - let block_hash = candidate.block.header().hash(); - let (collation, _) = collator_service - .build_collation(&last_head, block_hash, candidate) - .expect("collation to succeed."); - - request.complete(Some(CollationResult { collation, result_sender: None })); - } - }); - - spawner.spawn("cumulus-relay-driven-collator", None, collation_future); - - let msg = block_on(sub_rx.into_future()) - .0 - .expect("message should be send by `cumulus-relay-driven-collator` above."); - - let collator_fn = match msg { - CollationGenerationMessage::Initialize(CollationGenerationConfig { - collator: Some(c), - .. - }) => c, - _ => panic!("unexpected message or no collator fn"), - }; - - let validation_data = - PersistedValidationData { parent_head: header.encode().into(), ..Default::default() }; - let relay_parent = Default::default(); - - let collation = block_on(collator_fn(relay_parent, &validation_data)) - .expect("Collation is build") - .collation; - - let pov = collation.proof_of_validity.into_compressed(); - - let decompressed = - sp_maybe_compressed_blob::decompress(&pov.block_data.0, 1024 * 1024 * 10).unwrap(); - - let block = - ParachainBlockData::::decode(&mut &decompressed[..]).expect("Is a valid block"); - - assert_eq!(1, *block.blocks()[0].header().number()); - - // Ensure that we did not include `:code` in the proof. - let proof = block.proof().clone(); - - let backend = sp_state_machine::create_proof_check_backend::( - *header.state_root(), - proof.to_storage_proof::(None).unwrap().0, - ) - .unwrap(); - - // Should return an error, as it was not included while building the proof. - assert!(backend - .storage(sp_core::storage::well_known_keys::CODE) - .unwrap_err() - .contains("Trie lookup error: Database missing expected key")); - } -} From 8ca6df941f4b06510223c4a1688675cd05bfcbed Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Sat, 6 Sep 2025 00:35:57 +0100 Subject: [PATCH 11/22] remove unused type --- cumulus/client/service/src/lib.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/cumulus/client/service/src/lib.rs b/cumulus/client/service/src/lib.rs index 86593fd09ae8c..62199f2704162 100644 --- a/cumulus/client/service/src/lib.rs +++ b/cumulus/client/service/src/lib.rs @@ -118,20 +118,6 @@ pub struct StartRelayChainTasksParams<'a, Block: BlockT, Client, RCInterface> { pub prometheus_registry: Option<&'a Registry>, } -/// Parameters given to [`start_full_node`]. -pub struct StartFullNodeParams<'a, Block: BlockT, Client, RCInterface> { - pub para_id: ParaId, - pub client: Arc, - pub relay_chain_interface: RCInterface, - pub task_manager: &'a mut TaskManager, - pub announce_block: Arc>) + Send + Sync>, - pub relay_chain_slot_duration: Duration, - pub import_queue: Box>, - pub recovery_handle: Box, - pub sync_service: Arc>, - pub prometheus_registry: Option<&'a Registry>, -} - /// Start necessary consensus tasks related to the relay chain. /// /// Parachain nodes need to track the state of the relay chain and use the From 56d5550d3373a9df59438d9c045e8a6496e915b6 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Mon, 8 Sep 2025 22:05:52 +0100 Subject: [PATCH 12/22] Revert "remove old test" This reverts commit 8bcc054dbfbb74247a4d4a14d4dde0776ecf548d. --- cumulus/client/collator/src/lib.rs | 172 +++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index d2a4f34522b34..328e0bf772f3b 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -139,3 +139,175 @@ pub async fn initialize_collator_subsystems( .send_msg(CollatorProtocolMessage::CollateOn(para_id), "StartCollator") .await; } + +#[cfg(test)] +mod tests { + use super::*; + use crate::service::CollatorService; + use async_trait::async_trait; + use codec::{Decode, Encode}; + use cumulus_client_consensus_common::{ParachainCandidate, ParachainConsensus}; + use cumulus_primitives_core::{ + relay_chain::Hash as PHash, ParachainBlockData, PersistedValidationData, + }; + use cumulus_test_client::{ + Client, ClientBlockImportExt, DefaultTestClientBuilderExt, InitBlockBuilder, + TestClientBuilder, TestClientBuilderExt, + }; + use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; + use cumulus_test_runtime::{Block, Header}; + use futures::{channel::mpsc, executor::block_on, prelude::*, StreamExt}; + use polkadot_node_primitives::{CollationGenerationConfig, CollationResult}; + use polkadot_node_subsystem::messages::CollationGenerationMessage; + use polkadot_node_subsystem_test_helpers::ForwardSubsystem; + use polkadot_overseer::{dummy::dummy_overseer_builder, HeadSupportsParachains}; + use polkadot_primitives::HeadData; + use sp_consensus::BlockOrigin; + use sp_core::{testing::TaskExecutor, traits::SpawnNamed, Pair}; + use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}; + use sp_state_machine::Backend; + use std::sync::Arc; + + struct AlwaysSupportsParachains; + + #[async_trait] + impl HeadSupportsParachains for AlwaysSupportsParachains { + async fn head_supports_parachains(&self, _head: &PHash) -> bool { + true + } + } + + #[derive(Clone)] + struct DummyParachainConsensus { + client: Arc, + } + + #[async_trait::async_trait] + impl ParachainConsensus for DummyParachainConsensus { + async fn produce_candidate( + &mut self, + parent: &Header, + _: PHash, + validation_data: &PersistedValidationData, + ) -> Option> { + let mut sproof = RelayStateSproofBuilder::default(); + sproof.included_para_head = Some(HeadData(parent.encode())); + sproof.para_id = cumulus_test_runtime::PARACHAIN_ID.into(); + + let cumulus_test_client::BlockBuilderAndSupportData { block_builder, .. } = self + .client + .init_block_builder_at(parent.hash(), Some(validation_data.clone()), sproof); + + let (block, _, proof) = block_builder.build().expect("Creates block").into_inner(); + + self.client + .import(BlockOrigin::Own, block.clone()) + .await + .expect("Imports the block"); + + Some(ParachainCandidate { block, proof: proof.expect("Proof is returned") }) + } + } + + #[test] + fn collates_produces_a_block_and_storage_proof_does_not_contains_code() { + sp_tracing::try_init_simple(); + + let spawner = TaskExecutor::new(); + let para_id = ParaId::from(100); + let announce_block = |_, _| (); + let client = Arc::new(TestClientBuilder::new().build()); + let header = client.header(client.chain_info().genesis_hash).unwrap().unwrap(); + + let (sub_tx, sub_rx) = mpsc::channel(64); + + let (overseer, handle) = + dummy_overseer_builder(spawner.clone(), AlwaysSupportsParachains, None) + .expect("Creates overseer builder") + .replace_collation_generation(|_| ForwardSubsystem(sub_tx)) + .build() + .expect("Builds overseer"); + + spawner.spawn("overseer", None, overseer.run().then(|_| async {}).boxed()); + + let collator_service = CollatorService::new( + client.clone(), + Arc::new(spawner.clone()), + Arc::new(announce_block), + client.clone(), + ); + let mut parachain_consensus = Box::new(DummyParachainConsensus { client }); + + let collation_future = Box::pin(async move { + let mut request_stream = relay_chain_driven::init( + CollatorPair::generate().0, + para_id, + OverseerHandle::new(handle), + ) + .await; + while let Some(request) = request_stream.next().await { + let validation_data = request.persisted_validation_data().clone(); + let last_head = + ::Header::decode(&mut &validation_data.parent_head.0[..]) + .expect("decode header"); + let candidate = parachain_consensus + .produce_candidate(&last_head, *request.relay_parent(), &validation_data) + .await + .expect("candidate to be produced."); + let block_hash = candidate.block.header().hash(); + let (collation, _) = collator_service + .build_collation(&last_head, block_hash, candidate) + .expect("collation to succeed."); + + request.complete(Some(CollationResult { collation, result_sender: None })); + } + }); + + spawner.spawn("cumulus-relay-driven-collator", None, collation_future); + + let msg = block_on(sub_rx.into_future()) + .0 + .expect("message should be send by `cumulus-relay-driven-collator` above."); + + let collator_fn = match msg { + CollationGenerationMessage::Initialize(CollationGenerationConfig { + collator: Some(c), + .. + }) => c, + _ => panic!("unexpected message or no collator fn"), + }; + + let validation_data = + PersistedValidationData { parent_head: header.encode().into(), ..Default::default() }; + let relay_parent = Default::default(); + + let collation = block_on(collator_fn(relay_parent, &validation_data)) + .expect("Collation is build") + .collation; + + let pov = collation.proof_of_validity.into_compressed(); + + let decompressed = + sp_maybe_compressed_blob::decompress(&pov.block_data.0, 1024 * 1024 * 10).unwrap(); + + let block = + ParachainBlockData::::decode(&mut &decompressed[..]).expect("Is a valid block"); + + assert_eq!(1, *block.blocks()[0].header().number()); + + // Ensure that we did not include `:code` in the proof. + let proof = block.proof().clone(); + + let backend = sp_state_machine::create_proof_check_backend::( + *header.state_root(), + proof.to_storage_proof::(None).unwrap().0, + ) + .unwrap(); + + // Should return an error, as it was not included while building the proof. + assert!(backend + .storage(sp_core::storage::well_known_keys::CODE) + .unwrap_err() + .contains("Trie lookup error: Database missing expected key")); + } +} From dba2cc6fff6412c7cc40daae7bc3a82cf0643f72 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Mon, 8 Sep 2025 22:28:07 +0100 Subject: [PATCH 13/22] Revert "Revert "remove old test"" This reverts commit 56d5550d3373a9df59438d9c045e8a6496e915b6. --- cumulus/client/collator/src/lib.rs | 172 ----------------------------- 1 file changed, 172 deletions(-) diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 328e0bf772f3b..d2a4f34522b34 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -139,175 +139,3 @@ pub async fn initialize_collator_subsystems( .send_msg(CollatorProtocolMessage::CollateOn(para_id), "StartCollator") .await; } - -#[cfg(test)] -mod tests { - use super::*; - use crate::service::CollatorService; - use async_trait::async_trait; - use codec::{Decode, Encode}; - use cumulus_client_consensus_common::{ParachainCandidate, ParachainConsensus}; - use cumulus_primitives_core::{ - relay_chain::Hash as PHash, ParachainBlockData, PersistedValidationData, - }; - use cumulus_test_client::{ - Client, ClientBlockImportExt, DefaultTestClientBuilderExt, InitBlockBuilder, - TestClientBuilder, TestClientBuilderExt, - }; - use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; - use cumulus_test_runtime::{Block, Header}; - use futures::{channel::mpsc, executor::block_on, prelude::*, StreamExt}; - use polkadot_node_primitives::{CollationGenerationConfig, CollationResult}; - use polkadot_node_subsystem::messages::CollationGenerationMessage; - use polkadot_node_subsystem_test_helpers::ForwardSubsystem; - use polkadot_overseer::{dummy::dummy_overseer_builder, HeadSupportsParachains}; - use polkadot_primitives::HeadData; - use sp_consensus::BlockOrigin; - use sp_core::{testing::TaskExecutor, traits::SpawnNamed, Pair}; - use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}; - use sp_state_machine::Backend; - use std::sync::Arc; - - struct AlwaysSupportsParachains; - - #[async_trait] - impl HeadSupportsParachains for AlwaysSupportsParachains { - async fn head_supports_parachains(&self, _head: &PHash) -> bool { - true - } - } - - #[derive(Clone)] - struct DummyParachainConsensus { - client: Arc, - } - - #[async_trait::async_trait] - impl ParachainConsensus for DummyParachainConsensus { - async fn produce_candidate( - &mut self, - parent: &Header, - _: PHash, - validation_data: &PersistedValidationData, - ) -> Option> { - let mut sproof = RelayStateSproofBuilder::default(); - sproof.included_para_head = Some(HeadData(parent.encode())); - sproof.para_id = cumulus_test_runtime::PARACHAIN_ID.into(); - - let cumulus_test_client::BlockBuilderAndSupportData { block_builder, .. } = self - .client - .init_block_builder_at(parent.hash(), Some(validation_data.clone()), sproof); - - let (block, _, proof) = block_builder.build().expect("Creates block").into_inner(); - - self.client - .import(BlockOrigin::Own, block.clone()) - .await - .expect("Imports the block"); - - Some(ParachainCandidate { block, proof: proof.expect("Proof is returned") }) - } - } - - #[test] - fn collates_produces_a_block_and_storage_proof_does_not_contains_code() { - sp_tracing::try_init_simple(); - - let spawner = TaskExecutor::new(); - let para_id = ParaId::from(100); - let announce_block = |_, _| (); - let client = Arc::new(TestClientBuilder::new().build()); - let header = client.header(client.chain_info().genesis_hash).unwrap().unwrap(); - - let (sub_tx, sub_rx) = mpsc::channel(64); - - let (overseer, handle) = - dummy_overseer_builder(spawner.clone(), AlwaysSupportsParachains, None) - .expect("Creates overseer builder") - .replace_collation_generation(|_| ForwardSubsystem(sub_tx)) - .build() - .expect("Builds overseer"); - - spawner.spawn("overseer", None, overseer.run().then(|_| async {}).boxed()); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(spawner.clone()), - Arc::new(announce_block), - client.clone(), - ); - let mut parachain_consensus = Box::new(DummyParachainConsensus { client }); - - let collation_future = Box::pin(async move { - let mut request_stream = relay_chain_driven::init( - CollatorPair::generate().0, - para_id, - OverseerHandle::new(handle), - ) - .await; - while let Some(request) = request_stream.next().await { - let validation_data = request.persisted_validation_data().clone(); - let last_head = - ::Header::decode(&mut &validation_data.parent_head.0[..]) - .expect("decode header"); - let candidate = parachain_consensus - .produce_candidate(&last_head, *request.relay_parent(), &validation_data) - .await - .expect("candidate to be produced."); - let block_hash = candidate.block.header().hash(); - let (collation, _) = collator_service - .build_collation(&last_head, block_hash, candidate) - .expect("collation to succeed."); - - request.complete(Some(CollationResult { collation, result_sender: None })); - } - }); - - spawner.spawn("cumulus-relay-driven-collator", None, collation_future); - - let msg = block_on(sub_rx.into_future()) - .0 - .expect("message should be send by `cumulus-relay-driven-collator` above."); - - let collator_fn = match msg { - CollationGenerationMessage::Initialize(CollationGenerationConfig { - collator: Some(c), - .. - }) => c, - _ => panic!("unexpected message or no collator fn"), - }; - - let validation_data = - PersistedValidationData { parent_head: header.encode().into(), ..Default::default() }; - let relay_parent = Default::default(); - - let collation = block_on(collator_fn(relay_parent, &validation_data)) - .expect("Collation is build") - .collation; - - let pov = collation.proof_of_validity.into_compressed(); - - let decompressed = - sp_maybe_compressed_blob::decompress(&pov.block_data.0, 1024 * 1024 * 10).unwrap(); - - let block = - ParachainBlockData::::decode(&mut &decompressed[..]).expect("Is a valid block"); - - assert_eq!(1, *block.blocks()[0].header().number()); - - // Ensure that we did not include `:code` in the proof. - let proof = block.proof().clone(); - - let backend = sp_state_machine::create_proof_check_backend::( - *header.state_root(), - proof.to_storage_proof::(None).unwrap().0, - ) - .unwrap(); - - // Should return an error, as it was not included while building the proof. - assert!(backend - .storage(sp_core::storage::well_known_keys::CODE) - .unwrap_err() - .contains("Trie lookup error: Database missing expected key")); - } -} From 8113e47c39f57150508c18c6988504992a9ce886 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Tue, 9 Sep 2025 16:50:56 +0100 Subject: [PATCH 14/22] use cumulus_test_service::Consensus::Aura in zombienet test --- .../zombienet-sdk/tests/zombie_ci/pov_recovery.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs index d054fa197a3f4..070f2bff286fb 100644 --- a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs +++ b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs @@ -189,22 +189,18 @@ async fn build_network_config() -> Result { ]) }) .with_collator(|c| { - c.with_name("alice") - .validator(true) - .with_args(build_collator_args(vec!["--use-null-consensus".into()])) + c.with_name("alice").validator(true) }) .with_collator(|c| { c.with_name("charlie").validator(false).with_args(build_collator_args(vec![])) }) .with_collator(|c| { c.with_name("eve").validator(true).with_args(build_collator_args(vec![ - "--fail-pov-recovery".into(), - "--use-null-consensus".into(), + "--fail-pov-recovery".into() ])) }) .with_collator(|c| { c.with_name("one").validator(true).with_args(build_collator_args(vec![ - "--use-null-consensus".into(), ("--relay-chain-rpc-url", rpc_urls.clone()).into() ])) }) From 6ba4252527c35b8456352effbcc1338a7840ecb3 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Tue, 9 Sep 2025 20:49:27 +0100 Subject: [PATCH 15/22] cleanup --- cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs index 070f2bff286fb..8c2e2e5fd555b 100644 --- a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs +++ b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs @@ -192,7 +192,7 @@ async fn build_network_config() -> Result { c.with_name("alice").validator(true) }) .with_collator(|c| { - c.with_name("charlie").validator(false).with_args(build_collator_args(vec![])) + c.with_name("charlie").validator(false) }) .with_collator(|c| { c.with_name("eve").validator(true).with_args(build_collator_args(vec![ From 5e3947443208d52b8a7a2554657599ca4a20b1b6 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Tue, 9 Sep 2025 20:50:10 +0100 Subject: [PATCH 16/22] re-enable zombienet-cumulus-0002-pov_recovery test --- .github/zombienet-flaky-tests | 1 - .github/zombienet-tests/zombienet_cumulus_tests.yml | 3 --- 2 files changed, 4 deletions(-) diff --git a/.github/zombienet-flaky-tests b/.github/zombienet-flaky-tests index b0e20ae77c4ad..dffaedac2ef65 100644 --- a/.github/zombienet-flaky-tests +++ b/.github/zombienet-flaky-tests @@ -7,7 +7,6 @@ zombienet-polkadot-functional-spam-statement-distribution-requests zombienet-polkadot-misc-0001-parachains-paritydb zombienet-polkadot-functional-duplicate-collations zombienet-substrate-0002-validators-warp-sync -zombienet-cumulus-0002-pov_recovery zombienet-cumulus-0006-rpc_collator_builds_blocks zombienet-cumulus-0009-elastic_scaling_pov_recovery zombienet-cumulus-0010-elastic_scaling_multiple_block_per_slot diff --git a/.github/zombienet-tests/zombienet_cumulus_tests.yml b/.github/zombienet-tests/zombienet_cumulus_tests.yml index b25d0ce5e414a..c22945ee35470 100644 --- a/.github/zombienet-tests/zombienet_cumulus_tests.yml +++ b/.github/zombienet-tests/zombienet_cumulus_tests.yml @@ -4,9 +4,6 @@ cumulus-image: "test-parachain" use-zombienet-sdk: true -# Disabled, occasionally fails -# Takes too much CPU when spawning all nodes at the same time -# Re-enable when https://github.com/paritytech/zombienet-sdk/issues/371 is implemented - job-name: "zombienet-cumulus-0002-pov_recovery" test-filter: "zombie_ci::pov_recovery::pov_recovery" runner-type: "large" From 155aebaea4444f0414a41ea6b77d473983a484ea Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Tue, 9 Sep 2025 21:49:48 +0100 Subject: [PATCH 17/22] try --disable-block-announcements --- .../zombienet-sdk/tests/zombie_ci/pov_recovery.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs index 8c2e2e5fd555b..4c1e28572b084 100644 --- a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs +++ b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs @@ -189,18 +189,22 @@ async fn build_network_config() -> Result { ]) }) .with_collator(|c| { - c.with_name("alice").validator(true) + c.with_name("alice") + .validator(true) + .with_args(build_collator_args(vec!["--disable-block-announcements".into()])) }) .with_collator(|c| { - c.with_name("charlie").validator(false) + c.with_name("charlie").validator(false).with_args(build_collator_args(vec![])) }) .with_collator(|c| { c.with_name("eve").validator(true).with_args(build_collator_args(vec![ - "--fail-pov-recovery".into() + "--fail-pov-recovery".into(), + "--disable-block-announcements".into(), ])) }) .with_collator(|c| { c.with_name("one").validator(true).with_args(build_collator_args(vec![ + "--disable-block-announcements".into(), ("--relay-chain-rpc-url", rpc_urls.clone()).into() ])) }) From 6557eeab4ee294bfb06ff94606fa99f1fcf8f6ef Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Wed, 10 Sep 2025 09:44:33 +0100 Subject: [PATCH 18/22] Revert "re-enable zombienet-cumulus-0002-pov_recovery test" This reverts commit 5e3947443208d52b8a7a2554657599ca4a20b1b6. --- .github/zombienet-flaky-tests | 1 + .github/zombienet-tests/zombienet_cumulus_tests.yml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.github/zombienet-flaky-tests b/.github/zombienet-flaky-tests index dffaedac2ef65..b0e20ae77c4ad 100644 --- a/.github/zombienet-flaky-tests +++ b/.github/zombienet-flaky-tests @@ -7,6 +7,7 @@ zombienet-polkadot-functional-spam-statement-distribution-requests zombienet-polkadot-misc-0001-parachains-paritydb zombienet-polkadot-functional-duplicate-collations zombienet-substrate-0002-validators-warp-sync +zombienet-cumulus-0002-pov_recovery zombienet-cumulus-0006-rpc_collator_builds_blocks zombienet-cumulus-0009-elastic_scaling_pov_recovery zombienet-cumulus-0010-elastic_scaling_multiple_block_per_slot diff --git a/.github/zombienet-tests/zombienet_cumulus_tests.yml b/.github/zombienet-tests/zombienet_cumulus_tests.yml index c22945ee35470..b25d0ce5e414a 100644 --- a/.github/zombienet-tests/zombienet_cumulus_tests.yml +++ b/.github/zombienet-tests/zombienet_cumulus_tests.yml @@ -4,6 +4,9 @@ cumulus-image: "test-parachain" use-zombienet-sdk: true +# Disabled, occasionally fails +# Takes too much CPU when spawning all nodes at the same time +# Re-enable when https://github.com/paritytech/zombienet-sdk/issues/371 is implemented - job-name: "zombienet-cumulus-0002-pov_recovery" test-filter: "zombie_ci::pov_recovery::pov_recovery" runner-type: "large" From ef9f8862ea0cc82681b0f53ba66500a97bf9de2c Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Thu, 11 Sep 2025 09:54:15 +0100 Subject: [PATCH 19/22] update test --- .../tests/zombie_ci/pov_recovery.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs index 4c1e28572b084..4eff425d76578 100644 --- a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs +++ b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs @@ -77,9 +77,9 @@ async fn pov_recovery() -> Result<(), anyhow::Error> { .is_ok()); } - // Wait (up to 10 seconds) until pattern occurs at least 20 times + // Wait (up to 10 seconds) until pattern occurs at least 10 times let options = LogLineCountOptions { - predicate: Arc::new(|n| n >= 20), + predicate: Arc::new(|n| n >= 10), timeout: Duration::from_secs(10), wait_until_timeout_elapses: false, }; @@ -129,20 +129,22 @@ async fn build_network_config() -> Result { // - synchronize only with validator-0 // - parachain nodes // - bob - // - collator which is the only one producing blocks + // - collator which produces blocks, but doesn't announce them // - alice - // - collator which doesn't produce blocks + // - collator which produces blocks, but doesn't announce them // - will need to recover the pov blocks through availability recovery // - charlie // - full node // - will need to recover the pov blocks through availability recovery // - eve - // - collator which doesn't produce blocks + // - collator which produces blocks, but doesn't announce them // - it fails recovery from time to time to test retries // - one - // - RPC collator which does not produce blocks + // - RPC collator which produces blocks, but doesn't announce them + // - Uses an external relay chain node // - two // - RPC full node + // - Uses an external relay chain node let config = NetworkConfigBuilder::new() .with_relaychain(|r| { let r = r @@ -191,20 +193,18 @@ async fn build_network_config() -> Result { .with_collator(|c| { c.with_name("alice") .validator(true) - .with_args(build_collator_args(vec!["--disable-block-announcements".into()])) + .with_args(build_collator_args(vec![])) }) .with_collator(|c| { c.with_name("charlie").validator(false).with_args(build_collator_args(vec![])) }) .with_collator(|c| { c.with_name("eve").validator(true).with_args(build_collator_args(vec![ - "--fail-pov-recovery".into(), - "--disable-block-announcements".into(), + "--fail-pov-recovery".into() ])) }) .with_collator(|c| { c.with_name("one").validator(true).with_args(build_collator_args(vec![ - "--disable-block-announcements".into(), ("--relay-chain-rpc-url", rpc_urls.clone()).into() ])) }) From d775e270a3ff19e334e6408bead5e548640e8e9e Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 12 Sep 2025 14:40:54 +0100 Subject: [PATCH 20/22] improve zombie_ci::pov_recovery::pov_recovery test --- .../tests/zombie_ci/pov_recovery.rs | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs index 4eff425d76578..d8469a7c9e2de 100644 --- a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs +++ b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs @@ -61,14 +61,7 @@ async fn pov_recovery() -> Result<(), anyhow::Error> { ) .await?; - for (name, timeout_secs) in [ - ("bob", 600u64), - ("alice", 600u64), - ("charlie", 600u64), - ("one", 800u64), - ("two", 800u64), - ("eve", 800u64), - ] { + for (name, timeout_secs) in [("bob", 600u64)] { log::info!("Checking block production for {name} within {timeout_secs}s"); assert!(network .get_node(name)? @@ -77,14 +70,16 @@ async fn pov_recovery() -> Result<(), anyhow::Error> { .is_ok()); } - // Wait (up to 10 seconds) until pattern occurs at least 10 times - let options = LogLineCountOptions { - predicate: Arc::new(|n| n >= 10), - timeout: Duration::from_secs(10), - wait_until_timeout_elapses: false, - }; + for (name, timeout_secs) in + [("alice", 600u64), ("charlie", 600u64), ("one", 800u64), ("two", 800u64), ("eve", 800u64)] + { + // Wait (up to 10 seconds) until pattern occurs at least 10 times + let options = LogLineCountOptions { + predicate: Arc::new(|n| n >= 20), + timeout: Duration::from_secs(timeout_secs), + wait_until_timeout_elapses: false, + }; - for name in ["one", "two", "eve", "charlie", "alice"] { log::info!("Ensuring blocks are imported using PoV recovery by {name}"); let result = network .get_node(name)? From fb842d2be807c8d66ab42edee123c3569b092d20 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 12 Sep 2025 14:41:53 +0100 Subject: [PATCH 21/22] fix typo --- cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs index d8469a7c9e2de..1c88692a3bc3e 100644 --- a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs +++ b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs @@ -73,7 +73,7 @@ async fn pov_recovery() -> Result<(), anyhow::Error> { for (name, timeout_secs) in [("alice", 600u64), ("charlie", 600u64), ("one", 800u64), ("two", 800u64), ("eve", 800u64)] { - // Wait (up to 10 seconds) until pattern occurs at least 10 times + // Wait (up to 10 seconds) until pattern occurs at least 20 times let options = LogLineCountOptions { predicate: Arc::new(|n| n >= 20), timeout: Duration::from_secs(timeout_secs), From e3ebeb13378d54209ff9f6d7cd90735ab3a62a9a Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas Date: Fri, 12 Sep 2025 14:42:30 +0100 Subject: [PATCH 22/22] fix typo --- cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs index 1c88692a3bc3e..2d10cfbb038fc 100644 --- a/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs +++ b/cumulus/zombienet/zombienet-sdk/tests/zombie_ci/pov_recovery.rs @@ -73,7 +73,7 @@ async fn pov_recovery() -> Result<(), anyhow::Error> { for (name, timeout_secs) in [("alice", 600u64), ("charlie", 600u64), ("one", 800u64), ("two", 800u64), ("eve", 800u64)] { - // Wait (up to 10 seconds) until pattern occurs at least 20 times + // Wait (up to timeout_secs) until pattern occurs at least 20 times let options = LogLineCountOptions { predicate: Arc::new(|n| n >= 20), timeout: Duration::from_secs(timeout_secs),