astar_collator/parachain/
service.rs

1// This file is part of Astar.
2
3// Copyright (C) Stake Technologies Pte.Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6// Astar is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// Astar is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with Astar. If not, see <http://www.gnu.org/licenses/>.
18
19//! Parachain Service and ServiceFactory implementation.
20
21use astar_primitives::*;
22use cumulus_client_bootnodes::{start_bootnode_tasks, StartBootnodeTasksParams};
23use cumulus_client_cli::CollatorOptions;
24use cumulus_client_consensus_aura::collators::slot_based::{
25    self as aura, Params as AuraParams, SlotBasedBlockImport, SlotBasedBlockImportHandle,
26};
27use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport;
28use cumulus_client_consensus_relay_chain::Verifier as RelayChainVerifier;
29use cumulus_client_service::ParachainTracingExecuteBlock;
30use cumulus_client_service::{
31    prepare_node_config, start_relay_chain_tasks, BuildNetworkParams, DARecoveryProfile,
32    StartRelayChainTasksParams,
33};
34use cumulus_primitives_core::{
35    relay_chain::{CollatorPair, ValidationCode},
36    ParaId,
37};
38use cumulus_relay_chain_interface::RelayChainInterface;
39use fc_consensus::FrontierBlockImport as TFrontierBlockImport;
40use fc_rpc_core::types::{FeeHistoryCache, FilterPool};
41use fc_storage::StorageOverrideHandler;
42use futures::StreamExt;
43use sc_client_api::BlockchainEvents;
44use sc_client_db::PruningMode;
45use sc_consensus::{import_queue::BasicQueue, ImportQueue};
46use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY};
47use sc_network::{config::NetworkBackendType, NetworkBackend, NetworkBlock, PeerId};
48use sc_network_sync::SyncingService;
49use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager};
50use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle};
51use sp_api::{ApiExt, ProvideRuntimeApi};
52use sp_consensus_aura::{
53    sr25519::AuthorityId as AuraId, sr25519::AuthorityPair as AuraPair, AuraApi,
54};
55use sp_keystore::KeystorePtr;
56use sp_runtime::{traits::Block as BlockT, Percent};
57use std::{collections::BTreeMap, sync::Arc, time::Duration};
58use substrate_prometheus_endpoint::Registry;
59
60use super::shell_upgrade::*;
61
62use crate::{
63    evm_tracing_types::{EthApi as EthApiCmd, FrontierConfig},
64    rpc::tracing,
65};
66
67/// Parachain host functions
68#[cfg(feature = "runtime-benchmarks")]
69pub type HostFunctions = (
70    frame_benchmarking::benchmarking::HostFunctions,
71    cumulus_client_service::ParachainHostFunctions,
72    moonbeam_primitives_ext::moonbeam_ext::HostFunctions,
73);
74
75/// Parachain host functions
76#[cfg(not(feature = "runtime-benchmarks"))]
77pub type HostFunctions = (
78    cumulus_client_service::ParachainHostFunctions,
79    moonbeam_primitives_ext::moonbeam_ext::HostFunctions,
80);
81
82/// Parachain executor
83pub type ParachainExecutor = WasmExecutor<HostFunctions>;
84
85type FullClient =
86    TFullClient<Block, crate::parachain::fake_runtime_api::RuntimeApi, ParachainExecutor>;
87
88/// Frontier block import, wrapping the inner client.
89type FrontierBlockImportType = TFrontierBlockImport<Block, Arc<FullClient>, FullClient>;
90
91/// Slot-based block import sitting on top of Frontier.
92type SlotBasedImport = SlotBasedBlockImport<Block, FrontierBlockImportType, FullClient>;
93
94/// The full parachain block-import.
95type ParachainBlockImport = TParachainBlockImport<Block, SlotBasedImport, TFullBackend<Block>>;
96
97/// Starts a `ServiceBuilder` for a full service.
98///
99/// Use this macro if you don't actually need the full service, but just the builder in order to
100/// be able to perform chain operations.
101pub fn new_partial(
102    config: &Configuration,
103    evm_tracing_config: &FrontierConfig,
104) -> Result<
105    PartialComponents<
106        FullClient,
107        TFullBackend<Block>,
108        (),
109        sc_consensus::DefaultImportQueue<Block>,
110        sc_transaction_pool::TransactionPoolHandle<Block, FullClient>,
111        (
112            ParachainBlockImport,
113            SlotBasedBlockImportHandle<Block>,
114            Option<Telemetry>,
115            Option<TelemetryWorkerHandle>,
116            Arc<fc_db::Backend<Block, FullClient>>,
117        ),
118    >,
119    sc_service::Error,
120> {
121    let telemetry = config
122        .telemetry_endpoints
123        .clone()
124        .filter(|x| !x.is_empty())
125        .map(|endpoints| -> Result<_, sc_telemetry::Error> {
126            let worker = TelemetryWorker::new(16)?;
127            let telemetry = worker.handle().new_telemetry(endpoints);
128            Ok((worker, telemetry))
129        })
130        .transpose()?;
131
132    let heap_pages = config
133        .executor
134        .default_heap_pages
135        .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static {
136            extra_pages: h as _,
137        });
138
139    let executor = ParachainExecutor::builder()
140        .with_execution_method(config.executor.wasm_method)
141        .with_onchain_heap_alloc_strategy(heap_pages)
142        .with_offchain_heap_alloc_strategy(heap_pages)
143        .with_max_runtime_instances(config.executor.max_runtime_instances)
144        .with_runtime_cache_size(config.executor.runtime_cache_size)
145        .build();
146
147    let (client, backend, keystore_container, task_manager) =
148        sc_service::new_full_parts_record_import::<Block, _, _>(
149            config,
150            telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
151            executor,
152            true,
153            Default::default(),
154        )?;
155    let client = Arc::new(client);
156
157    let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle());
158
159    let telemetry = telemetry.map(|(worker, telemetry)| {
160        task_manager
161            .spawn_handle()
162            .spawn("telemetry", None, worker.run());
163        telemetry
164    });
165
166    let transaction_pool = sc_transaction_pool::Builder::new(
167        task_manager.spawn_essential_handle(),
168        client.clone(),
169        config.role.is_authority().into(),
170    )
171    .with_options(config.transaction_pool.clone())
172    .with_prometheus(config.prometheus_registry())
173    .build();
174
175    let frontier_backend = Arc::new(crate::rpc::open_frontier_backend(
176        client.clone(),
177        config,
178        evm_tracing_config,
179    )?);
180
181    let frontier_block_import = TFrontierBlockImport::new(client.clone(), client.clone());
182    let (slot_based_block_import, slot_based_import_handle) =
183        SlotBasedBlockImport::new(frontier_block_import, client.clone());
184    let parachain_block_import: ParachainBlockImport =
185        ParachainBlockImport::new(slot_based_block_import, backend.clone());
186
187    let import_queue = build_import_queue(
188        client.clone(),
189        parachain_block_import.clone(),
190        config,
191        telemetry.as_ref().map(|telemetry| telemetry.handle()),
192        &task_manager,
193    );
194
195    let params = PartialComponents {
196        backend,
197        client,
198        import_queue,
199        keystore_container,
200        task_manager,
201        transaction_pool: transaction_pool.into(),
202        select_chain: (),
203        other: (
204            parachain_block_import,
205            slot_based_import_handle,
206            telemetry,
207            telemetry_worker_handle,
208            frontier_backend,
209        ),
210    };
211
212    Ok(params)
213}
214
215#[derive(Clone)]
216/// To add additional config to start_xyz_node functions
217pub struct AdditionalConfig {
218    /// EVM tracing configuration
219    pub evm_tracing_config: FrontierConfig,
220
221    /// Whether EVM RPC be enabled
222    pub enable_evm_rpc: bool,
223
224    /// Maxium allowed block size limit to propose
225    pub proposer_block_size_limit: usize,
226
227    /// Soft deadline limit used by `Proposer`
228    pub proposer_soft_deadline_percent: u8,
229
230    /// Hardware benchmarks score
231    pub hwbench: Option<sc_sysinfo::HwBench>,
232}
233
234/// Start a node with the given parachain `Configuration` and relay chain `Configuration`.
235///
236/// This is the actual implementation that is abstract over the executor and the runtime api.
237#[sc_tracing::logging::prefix_logs_with("Parachain")]
238async fn start_node_impl<N>(
239    parachain_config: Configuration,
240    polkadot_config: Configuration,
241    collator_options: CollatorOptions,
242    para_id: ParaId,
243    additional_config: AdditionalConfig,
244) -> sc_service::error::Result<(TaskManager, Arc<FullClient>)>
245where
246    N: NetworkBackend<Block, <Block as BlockT>::Hash>,
247{
248    let parachain_config = prepare_node_config(parachain_config);
249
250    let PartialComponents {
251        client,
252        backend,
253        mut task_manager,
254        keystore_container,
255        select_chain: _,
256        import_queue,
257        transaction_pool,
258        other:
259            (
260                parachain_block_import,
261                block_import_handle,
262                mut telemetry,
263                telemetry_worker_handle,
264                frontier_backend,
265            ),
266    } = new_partial(&parachain_config, &additional_config.evm_tracing_config)?;
267
268    let prometheus_registry = parachain_config.prometheus_registry().cloned();
269    let net_config = sc_network::config::FullNetworkConfiguration::<_, _, N>::new(
270        &parachain_config.network,
271        prometheus_registry.clone(),
272    );
273
274    let metrics = N::register_notification_metrics(
275        parachain_config
276            .prometheus_config
277            .as_ref()
278            .map(|cfg| &cfg.registry),
279    );
280
281    // Extract relay_chain_fork_id before polkadot_config is moved into the builder.
282    let relay_chain_fork_id = polkadot_config
283        .chain_spec
284        .fork_id()
285        .map(ToString::to_string);
286
287    let (relay_chain_interface, collator_key, relay_chain_network, paranode_rx) =
288        cumulus_client_service::build_relay_chain_interface(
289            polkadot_config,
290            &parachain_config,
291            telemetry_worker_handle,
292            &mut task_manager,
293            collator_options.clone(),
294            additional_config.hwbench.clone(),
295        )
296        .await
297        .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?;
298
299    let is_authority = parachain_config.role.is_authority();
300    let import_queue_service = import_queue.service();
301    let (network, system_rpc_tx, tx_handler_controller, sync_service) =
302        cumulus_client_service::build_network(BuildNetworkParams {
303            parachain_config: &parachain_config,
304            net_config,
305            para_id,
306            client: client.clone(),
307            transaction_pool: transaction_pool.clone(),
308            spawn_handle: task_manager.spawn_handle(),
309            spawn_essential_handle: task_manager.spawn_essential_handle(),
310            import_queue,
311            relay_chain_interface: relay_chain_interface.clone(),
312            sybil_resistance_level: cumulus_client_service::CollatorSybilResistance::Resistant,
313            metrics,
314        })
315        .await?;
316
317    let filter_pool: FilterPool = Arc::new(std::sync::Mutex::new(BTreeMap::new()));
318    let fee_history_cache: FeeHistoryCache = Arc::new(std::sync::Mutex::new(BTreeMap::new()));
319    let storage_override = Arc::new(StorageOverrideHandler::new(client.clone()));
320
321    // Sinks for pubsub notifications.
322    // Everytime a new subscription is created, a new mpsc channel is added to the sink pool.
323    // The MappingSyncWorker sends through the channel on block import and the subscription emits a notification to the subscriber on receiving a message through this channel.
324    // This way we avoid race conditions when using native substrate block import notification stream.
325    let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks<
326        fc_mapping_sync::EthereumBlockNotification<Block>,
327    > = Default::default();
328    let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks);
329
330    let ethapi_cmd = additional_config.evm_tracing_config.ethapi.clone();
331    let tracing_requesters =
332        if ethapi_cmd.contains(&EthApiCmd::Debug) || ethapi_cmd.contains(&EthApiCmd::Trace) {
333            tracing::spawn_tracing_tasks(
334                &additional_config.evm_tracing_config,
335                prometheus_registry.clone(),
336                tracing::SpawnTasksParams {
337                    task_manager: &task_manager,
338                    client: client.clone(),
339                    substrate_backend: backend.clone(),
340                    frontier_backend: frontier_backend.clone(),
341                    storage_override: storage_override.clone(),
342                },
343            )
344        } else {
345            tracing::RpcRequesters {
346                debug: None,
347                trace: None,
348            }
349        };
350
351    // Frontier offchain DB task. Essential.
352    // Maps emulated ethereum data to substrate native data.
353    match frontier_backend.as_ref() {
354        fc_db::Backend::KeyValue(ref b) => {
355            task_manager.spawn_essential_handle().spawn(
356                "frontier-mapping-sync-worker",
357                Some("frontier"),
358                fc_mapping_sync::kv::MappingSyncWorker::new(
359                    client.import_notification_stream(),
360                    Duration::new(6, 0),
361                    client.clone(),
362                    backend.clone(),
363                    storage_override.clone(),
364                    b.clone(),
365                    3,
366                    0,
367                    parachain_config.state_pruning.clone().and_then(|mode| {
368                        if let PruningMode::Constrained(c) = mode {
369                            c.max_blocks.map(u64::from)
370                        } else {
371                            None
372                        }
373                    }),
374                    fc_mapping_sync::SyncStrategy::Parachain,
375                    sync_service.clone(),
376                    pubsub_notification_sinks.clone(),
377                )
378                .for_each(|()| futures::future::ready(())),
379            );
380        }
381        fc_db::Backend::Sql(ref b) => {
382            task_manager.spawn_essential_handle().spawn_blocking(
383                "frontier-mapping-sync-worker",
384                Some("frontier"),
385                fc_mapping_sync::sql::SyncWorker::run(
386                    client.clone(),
387                    backend.clone(),
388                    b.clone(),
389                    client.import_notification_stream(),
390                    fc_mapping_sync::sql::SyncWorkerConfig {
391                        read_notification_timeout: Duration::from_secs(10),
392                        check_indexed_blocks_interval: Duration::from_secs(60),
393                    },
394                    fc_mapping_sync::SyncStrategy::Parachain,
395                    sync_service.clone(),
396                    pubsub_notification_sinks.clone(),
397                ),
398            );
399        }
400    }
401
402    // Frontier `EthFilterApi` maintenance. Manages the pool of user-created Filters.
403    // Each filter is allowed to stay in the pool for 100 blocks.
404    const FILTER_RETAIN_THRESHOLD: u64 = 100;
405    task_manager.spawn_essential_handle().spawn(
406        "frontier-filter-pool",
407        Some("frontier"),
408        fc_rpc::EthTask::filter_pool_task(
409            client.clone(),
410            filter_pool.clone(),
411            FILTER_RETAIN_THRESHOLD,
412        ),
413    );
414
415    const FEE_HISTORY_LIMIT: u64 = 2048;
416    task_manager.spawn_essential_handle().spawn(
417        "frontier-fee-history",
418        Some("frontier"),
419        fc_rpc::EthTask::fee_history_task(
420            client.clone(),
421            storage_override.clone(),
422            fee_history_cache.clone(),
423            FEE_HISTORY_LIMIT,
424        ),
425    );
426
427    let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new(
428        task_manager.spawn_handle(),
429        storage_override.clone(),
430        50,
431        50,
432        prometheus_registry.clone(),
433    ));
434
435    let rpc_extensions_builder = {
436        let client = client.clone();
437        let network = network.clone();
438        let transaction_pool = transaction_pool.clone();
439        let rpc_config = crate::rpc::EvmTracingConfig {
440            tracing_requesters,
441            trace_filter_max_count: additional_config.evm_tracing_config.ethapi_trace_max_count,
442            trace_filter_max_block_range: additional_config
443                .evm_tracing_config
444                .trace_filter_max_block_range,
445            enable_txpool: ethapi_cmd.contains(&EthApiCmd::TxPool),
446        };
447        let sync = sync_service.clone();
448        let pubsub_notification_sinks = pubsub_notification_sinks.clone();
449
450        Box::new(move |subscription| {
451            let deps = crate::rpc::FullDeps {
452                client: client.clone(),
453                pool: transaction_pool.clone(),
454                graph: transaction_pool.clone(),
455                network: network.clone(),
456                sync: sync.clone(),
457                is_authority,
458                frontier_backend: match *frontier_backend {
459                    fc_db::Backend::KeyValue(ref b) => b.clone(),
460                    fc_db::Backend::Sql(ref b) => b.clone(),
461                },
462                filter_pool: filter_pool.clone(),
463                fee_history_limit: FEE_HISTORY_LIMIT,
464                fee_history_cache: fee_history_cache.clone(),
465                block_data_cache: block_data_cache.clone(),
466                storage_override: storage_override.clone(),
467                enable_evm_rpc: additional_config.enable_evm_rpc,
468                command_sink: None,
469            };
470
471            crate::rpc::create_full(
472                deps,
473                subscription,
474                pubsub_notification_sinks.clone(),
475                rpc_config.clone(),
476            )
477            .map_err(Into::into)
478        })
479    };
480
481    // Extract values needed after `parachain_config` is moved into `spawn_tasks`.
482    let parachain_advertise_non_global_ips = parachain_config.network.allow_non_globals_in_dht;
483    let parachain_fork_id = parachain_config
484        .chain_spec
485        .fork_id()
486        .map(ToString::to_string);
487    let parachain_public_addresses = parachain_config.network.public_addresses.clone();
488
489    // Spawn basic services.
490    sc_service::spawn_tasks(sc_service::SpawnTasksParams {
491        rpc_builder: rpc_extensions_builder,
492        client: client.clone(),
493        transaction_pool: transaction_pool.clone(),
494        task_manager: &mut task_manager,
495        config: parachain_config,
496        keystore: keystore_container.keystore(),
497        backend: backend.clone(),
498        network: network.clone(),
499        system_rpc_tx,
500        sync_service: sync_service.clone(),
501        tx_handler_controller,
502        telemetry: telemetry.as_mut(),
503        tracing_execute_block: Some(Arc::new(ParachainTracingExecuteBlock::new(client.clone()))),
504    })?;
505
506    if let Some(hwbench) = additional_config.hwbench.clone() {
507        sc_sysinfo::print_hwbench(&hwbench);
508        if is_authority {
509            warn_if_slow_hardware(&hwbench);
510        }
511
512        if let Some(ref mut telemetry) = telemetry {
513            let telemetry_handle = telemetry.handle();
514            task_manager.spawn_handle().spawn(
515                "telemetry_hwbench",
516                None,
517                sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench),
518            );
519        }
520    }
521
522    let announce_block = {
523        let sync_service = sync_service.clone();
524        Arc::new(move |hash, data| sync_service.announce_block(hash, data))
525    };
526
527    let overseer_handle = relay_chain_interface
528        .overseer_handle()
529        .map_err(|e| sc_service::Error::Application(Box::new(e)))?;
530
531    start_relay_chain_tasks(StartRelayChainTasksParams {
532        client: client.clone(),
533        announce_block: announce_block.clone(),
534        task_manager: &mut task_manager,
535        para_id,
536        relay_chain_interface: relay_chain_interface.clone(),
537        relay_chain_slot_duration: Duration::from_secs(6),
538        import_queue: import_queue_service,
539        recovery_handle: Box::new(overseer_handle.clone()),
540        sync_service: sync_service.clone(),
541        da_recovery_profile: if is_authority {
542            DARecoveryProfile::Collator
543        } else {
544            DARecoveryProfile::FullNode
545        },
546        prometheus_registry: prometheus_registry.as_ref(),
547    })?;
548
549    start_bootnode_tasks(StartBootnodeTasksParams {
550        embedded_dht_bootnode: collator_options.embedded_dht_bootnode,
551        dht_bootnode_discovery: collator_options.dht_bootnode_discovery,
552        para_id,
553        task_manager: &mut task_manager,
554        relay_chain_interface: relay_chain_interface.clone(),
555        relay_chain_fork_id,
556        relay_chain_network,
557        request_receiver: paranode_rx,
558        parachain_network: network.clone(),
559        advertise_non_global_ips: parachain_advertise_non_global_ips,
560        parachain_genesis_hash: client.chain_info().genesis_hash.as_ref().to_vec(),
561        parachain_fork_id,
562        parachain_public_addresses,
563    });
564
565    if is_authority {
566        start_aura_consensus(
567            client.clone(),
568            backend,
569            parachain_block_import,
570            block_import_handle,
571            prometheus_registry.as_ref(),
572            telemetry.map(|t| t.handle()),
573            &mut task_manager,
574            relay_chain_interface,
575            transaction_pool,
576            sync_service,
577            keystore_container.keystore(),
578            para_id,
579            collator_key.expect("Command line arguments do not allow this. qed"),
580            network.local_peer_id(),
581            additional_config,
582        )?;
583    }
584
585    Ok((task_manager, client))
586}
587
588/// Build aura import queue with fallback to relay-chain verifier.
589/// Starts with relay-chain verifier until aura becomes available.
590pub fn build_import_queue(
591    client: Arc<FullClient>,
592    block_import: ParachainBlockImport,
593    config: &Configuration,
594    telemetry_handle: Option<TelemetryHandle>,
595    task_manager: &TaskManager,
596) -> sc_consensus::DefaultImportQueue<Block> {
597    let verifier_client = client.clone();
598    // CIDP for Aura verifier - requires AuraApi for slot_duration
599    let create_aura_inherent_data_providers = move |parent_hash, _| {
600        let cidp_client = verifier_client.clone();
601        async move {
602            let slot_duration =
603                cumulus_client_consensus_aura::slot_duration_at(&*cidp_client, parent_hash)?;
604            let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
605
606            let slot =
607                sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration(
608                    *timestamp,
609                    slot_duration,
610                );
611
612            Ok((slot, timestamp))
613        }
614    };
615
616    // CIDP for relay chain verifier - minimal, only timestamp (no AuraApi required)
617    // This is used for pre-AuraApi blocks (Shiden genesis did not start with AuraApi)
618    let create_relay_inherent_data_providers = move |_parent_hash: Hash, _| async move {
619        let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
620        Ok(timestamp)
621    };
622
623    let aura_verifier = Box::new(cumulus_client_consensus_aura::build_verifier::<
624        AuraPair,
625        _,
626        _,
627        _,
628    >(cumulus_client_consensus_aura::BuildVerifierParams {
629        client: client.clone(),
630        create_inherent_data_providers: create_aura_inherent_data_providers,
631        telemetry: telemetry_handle,
632    }));
633
634    let relay_chain_verifier = Box::new(RelayChainVerifier::new(
635        client.clone(),
636        create_relay_inherent_data_providers,
637    )) as Box<_>;
638
639    let verifier = Verifier {
640        client,
641        relay_chain_verifier,
642        aura_verifier,
643    };
644
645    let registry = config.prometheus_registry();
646    let spawner = task_manager.spawn_essential_handle();
647
648    BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry)
649}
650
651/// Start collating with the `shell` runtime while waiting for an upgrade to an Aura compatible runtime.
652fn start_aura_consensus(
653    client: Arc<FullClient>,
654    backend: Arc<TFullBackend<Block>>,
655    block_import: ParachainBlockImport,
656    block_import_handle: SlotBasedBlockImportHandle<Block>,
657    prometheus_registry: Option<&Registry>,
658    telemetry: Option<TelemetryHandle>,
659    task_manager: &TaskManager,
660    relay_chain_interface: Arc<dyn RelayChainInterface>,
661    transaction_pool: Arc<sc_transaction_pool::TransactionPoolHandle<Block, FullClient>>,
662    sync_oracle: Arc<SyncingService<Block>>,
663    keystore: KeystorePtr,
664    para_id: ParaId,
665    collator_key: CollatorPair,
666    collator_peer_id: PeerId,
667    additional_config: AdditionalConfig,
668) -> Result<(), sc_service::Error> {
669    let mut proposer_factory = sc_basic_authorship::ProposerFactory::new(
670        task_manager.spawn_handle(),
671        client.clone(),
672        transaction_pool,
673        prometheus_registry,
674        telemetry,
675    );
676
677    proposer_factory.set_default_block_size_limit(additional_config.proposer_block_size_limit);
678    proposer_factory.set_soft_deadline(Percent::from_percent(
679        additional_config.proposer_soft_deadline_percent,
680    ));
681
682    let announce_block = {
683        let sync_service = sync_oracle.clone();
684        Arc::new(move |hash, data| sync_service.announce_block(hash, data))
685    };
686
687    let collator_service = cumulus_client_collator::service::CollatorService::new(
688        client.clone(),
689        Arc::new(task_manager.spawn_handle()),
690        announce_block,
691        client.clone(),
692    );
693
694    let params = AuraParams {
695        create_inherent_data_providers: move |_, ()| async move { Ok(()) },
696        block_import,
697        para_client: client.clone(),
698        para_backend: backend,
699        relay_client: relay_chain_interface.clone(),
700        code_hash_provider: {
701            let client = client.clone();
702            move |block_hash| {
703                client
704                    .code_at(block_hash)
705                    .ok()
706                    .map(|c| ValidationCode::from(c).hash())
707            }
708        },
709        keystore,
710        collator_key,
711        collator_peer_id,
712        para_id,
713        slot_offset: Duration::from_secs(1),
714        relay_chain_slot_duration: Duration::from_secs(6),
715        proposer: proposer_factory,
716        collator_service,
717        authoring_duration: Duration::from_millis(2000),
718        reinitialize: false,
719        block_import_handle,
720        spawner: task_manager.spawn_essential_handle(),
721        // If necessary, AdditionalConfig CLI params could be extend to make it configurable.
722        // However, it will be removed once https://github.com/paritytech/polkadot-sdk/issues/6020 is fixed.
723        max_pov_percentage: None, // default is 85%
724        export_pov: None,
725    };
726
727    let fut = async move {
728        wait_for_aura(client).await;
729        aura::run::<Block, AuraPair, _, _, _, _, _, _, _, _, _>(params);
730    };
731
732    task_manager.spawn_handle().spawn("aura", None, fut);
733    Ok(())
734}
735
736/// Wait for the Aura runtime API to appear on chain.
737/// This is useful for chains that started out without Aura. Components that
738/// are depending on Aura functionality will wait until Aura appears in the runtime.
739async fn wait_for_aura(client: Arc<FullClient>) {
740    let finalized_hash = client.chain_info().finalized_hash;
741    if client
742        .runtime_api()
743        .has_api::<dyn AuraApi<Block, AuraId>>(finalized_hash)
744        .unwrap_or_default()
745    {
746        return;
747    };
748
749    let mut stream = client.finality_notification_stream();
750    while let Some(notification) = stream.next().await {
751        if client
752            .runtime_api()
753            .has_api::<dyn AuraApi<Block, AuraId>>(notification.hash)
754            .unwrap_or_default()
755        {
756            return;
757        }
758    }
759}
760
761/// Checks that the hardware meets the requirements and print a warning otherwise.
762fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) {
763    // Polkadot para-chains should generally use these requirements to ensure that the relay-chain
764    // will not take longer than expected to import its blocks.
765    if let Err(err) =
766        frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench, false)
767    {
768        log::warn!(
769            "⚠️  The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\
770            https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware",
771            err
772        );
773    }
774}
775
776/// Start a parachain node.
777pub async fn start_node(
778    parachain_config: Configuration,
779    polkadot_config: Configuration,
780    collator_options: CollatorOptions,
781    para_id: ParaId,
782    additional_config: AdditionalConfig,
783) -> sc_service::error::Result<(TaskManager, Arc<FullClient>)> {
784    match parachain_config.network.network_backend {
785        NetworkBackendType::Libp2p => {
786            start_node_impl::<sc_network::NetworkWorker<_, _>>(
787                parachain_config,
788                polkadot_config,
789                collator_options,
790                para_id,
791                additional_config,
792            )
793            .await
794        }
795        NetworkBackendType::Litep2p => {
796            start_node_impl::<sc_network::Litep2pNetworkBackend>(
797                parachain_config,
798                polkadot_config,
799                collator_options,
800                para_id,
801                additional_config,
802            )
803            .await
804        }
805    }
806}