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        )?;
154    let client = Arc::new(client);
155
156    let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle());
157
158    let telemetry = telemetry.map(|(worker, telemetry)| {
159        task_manager
160            .spawn_handle()
161            .spawn("telemetry", None, worker.run());
162        telemetry
163    });
164
165    let transaction_pool = sc_transaction_pool::Builder::new(
166        task_manager.spawn_essential_handle(),
167        client.clone(),
168        config.role.is_authority().into(),
169    )
170    .with_options(config.transaction_pool.clone())
171    .with_prometheus(config.prometheus_registry())
172    .build();
173
174    let frontier_backend = Arc::new(crate::rpc::open_frontier_backend(
175        client.clone(),
176        config,
177        evm_tracing_config,
178    )?);
179
180    let frontier_block_import = TFrontierBlockImport::new(client.clone(), client.clone());
181    let (slot_based_block_import, slot_based_import_handle) =
182        SlotBasedBlockImport::new(frontier_block_import, client.clone());
183    let parachain_block_import: ParachainBlockImport =
184        ParachainBlockImport::new(slot_based_block_import, backend.clone());
185
186    let import_queue = build_import_queue(
187        client.clone(),
188        parachain_block_import.clone(),
189        config,
190        telemetry.as_ref().map(|telemetry| telemetry.handle()),
191        &task_manager,
192    );
193
194    let params = PartialComponents {
195        backend,
196        client,
197        import_queue,
198        keystore_container,
199        task_manager,
200        transaction_pool: transaction_pool.into(),
201        select_chain: (),
202        other: (
203            parachain_block_import,
204            slot_based_import_handle,
205            telemetry,
206            telemetry_worker_handle,
207            frontier_backend,
208        ),
209    };
210
211    Ok(params)
212}
213
214#[derive(Clone)]
215/// To add additional config to start_xyz_node functions
216pub struct AdditionalConfig {
217    /// EVM tracing configuration
218    pub evm_tracing_config: FrontierConfig,
219
220    /// Whether EVM RPC be enabled
221    pub enable_evm_rpc: bool,
222
223    /// Maxium allowed block size limit to propose
224    pub proposer_block_size_limit: usize,
225
226    /// Soft deadline limit used by `Proposer`
227    pub proposer_soft_deadline_percent: u8,
228
229    /// Hardware benchmarks score
230    pub hwbench: Option<sc_sysinfo::HwBench>,
231}
232
233/// Start a node with the given parachain `Configuration` and relay chain `Configuration`.
234///
235/// This is the actual implementation that is abstract over the executor and the runtime api.
236#[sc_tracing::logging::prefix_logs_with("Parachain")]
237async fn start_node_impl<N>(
238    parachain_config: Configuration,
239    polkadot_config: Configuration,
240    collator_options: CollatorOptions,
241    para_id: ParaId,
242    additional_config: AdditionalConfig,
243) -> sc_service::error::Result<(TaskManager, Arc<FullClient>)>
244where
245    N: NetworkBackend<Block, <Block as BlockT>::Hash>,
246{
247    let parachain_config = prepare_node_config(parachain_config);
248
249    let PartialComponents {
250        client,
251        backend,
252        mut task_manager,
253        keystore_container,
254        select_chain: _,
255        import_queue,
256        transaction_pool,
257        other:
258            (
259                parachain_block_import,
260                block_import_handle,
261                mut telemetry,
262                telemetry_worker_handle,
263                frontier_backend,
264            ),
265    } = new_partial(&parachain_config, &additional_config.evm_tracing_config)?;
266
267    let prometheus_registry = parachain_config.prometheus_registry().cloned();
268    let net_config = sc_network::config::FullNetworkConfiguration::<_, _, N>::new(
269        &parachain_config.network,
270        prometheus_registry.clone(),
271    );
272
273    let metrics = N::register_notification_metrics(
274        parachain_config
275            .prometheus_config
276            .as_ref()
277            .map(|cfg| &cfg.registry),
278    );
279
280    // Extract relay_chain_fork_id before polkadot_config is moved into the builder.
281    let relay_chain_fork_id = polkadot_config
282        .chain_spec
283        .fork_id()
284        .map(ToString::to_string);
285
286    let (relay_chain_interface, collator_key, relay_chain_network, paranode_rx) =
287        cumulus_client_service::build_relay_chain_interface(
288            polkadot_config,
289            &parachain_config,
290            telemetry_worker_handle,
291            &mut task_manager,
292            collator_options.clone(),
293            additional_config.hwbench.clone(),
294        )
295        .await
296        .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?;
297
298    let is_authority = parachain_config.role.is_authority();
299    let import_queue_service = import_queue.service();
300    let (network, system_rpc_tx, tx_handler_controller, sync_service) =
301        cumulus_client_service::build_network(BuildNetworkParams {
302            parachain_config: &parachain_config,
303            net_config,
304            para_id,
305            client: client.clone(),
306            transaction_pool: transaction_pool.clone(),
307            spawn_handle: task_manager.spawn_handle(),
308            import_queue,
309            relay_chain_interface: relay_chain_interface.clone(),
310            sybil_resistance_level: cumulus_client_service::CollatorSybilResistance::Resistant,
311            metrics,
312        })
313        .await?;
314
315    let filter_pool: FilterPool = Arc::new(std::sync::Mutex::new(BTreeMap::new()));
316    let fee_history_cache: FeeHistoryCache = Arc::new(std::sync::Mutex::new(BTreeMap::new()));
317    let storage_override = Arc::new(StorageOverrideHandler::new(client.clone()));
318
319    // Sinks for pubsub notifications.
320    // Everytime a new subscription is created, a new mpsc channel is added to the sink pool.
321    // 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.
322    // This way we avoid race conditions when using native substrate block import notification stream.
323    let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks<
324        fc_mapping_sync::EthereumBlockNotification<Block>,
325    > = Default::default();
326    let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks);
327
328    let ethapi_cmd = additional_config.evm_tracing_config.ethapi.clone();
329    let tracing_requesters =
330        if ethapi_cmd.contains(&EthApiCmd::Debug) || ethapi_cmd.contains(&EthApiCmd::Trace) {
331            tracing::spawn_tracing_tasks(
332                &additional_config.evm_tracing_config,
333                prometheus_registry.clone(),
334                tracing::SpawnTasksParams {
335                    task_manager: &task_manager,
336                    client: client.clone(),
337                    substrate_backend: backend.clone(),
338                    frontier_backend: frontier_backend.clone(),
339                    storage_override: storage_override.clone(),
340                },
341            )
342        } else {
343            tracing::RpcRequesters {
344                debug: None,
345                trace: None,
346            }
347        };
348
349    // Frontier offchain DB task. Essential.
350    // Maps emulated ethereum data to substrate native data.
351    match frontier_backend.as_ref() {
352        fc_db::Backend::KeyValue(ref b) => {
353            task_manager.spawn_essential_handle().spawn(
354                "frontier-mapping-sync-worker",
355                Some("frontier"),
356                fc_mapping_sync::kv::MappingSyncWorker::new(
357                    client.import_notification_stream(),
358                    Duration::new(6, 0),
359                    client.clone(),
360                    backend.clone(),
361                    storage_override.clone(),
362                    b.clone(),
363                    3,
364                    0,
365                    parachain_config.state_pruning.clone().and_then(|mode| {
366                        if let PruningMode::Constrained(c) = mode {
367                            c.max_blocks.map(u64::from)
368                        } else {
369                            None
370                        }
371                    }),
372                    fc_mapping_sync::SyncStrategy::Parachain,
373                    sync_service.clone(),
374                    pubsub_notification_sinks.clone(),
375                )
376                .for_each(|()| futures::future::ready(())),
377            );
378        }
379        fc_db::Backend::Sql(ref b) => {
380            task_manager.spawn_essential_handle().spawn_blocking(
381                "frontier-mapping-sync-worker",
382                Some("frontier"),
383                fc_mapping_sync::sql::SyncWorker::run(
384                    client.clone(),
385                    backend.clone(),
386                    b.clone(),
387                    client.import_notification_stream(),
388                    fc_mapping_sync::sql::SyncWorkerConfig {
389                        read_notification_timeout: Duration::from_secs(10),
390                        check_indexed_blocks_interval: Duration::from_secs(60),
391                    },
392                    fc_mapping_sync::SyncStrategy::Parachain,
393                    sync_service.clone(),
394                    pubsub_notification_sinks.clone(),
395                ),
396            );
397        }
398    }
399
400    // Frontier `EthFilterApi` maintenance. Manages the pool of user-created Filters.
401    // Each filter is allowed to stay in the pool for 100 blocks.
402    const FILTER_RETAIN_THRESHOLD: u64 = 100;
403    task_manager.spawn_essential_handle().spawn(
404        "frontier-filter-pool",
405        Some("frontier"),
406        fc_rpc::EthTask::filter_pool_task(
407            client.clone(),
408            filter_pool.clone(),
409            FILTER_RETAIN_THRESHOLD,
410        ),
411    );
412
413    const FEE_HISTORY_LIMIT: u64 = 2048;
414    task_manager.spawn_essential_handle().spawn(
415        "frontier-fee-history",
416        Some("frontier"),
417        fc_rpc::EthTask::fee_history_task(
418            client.clone(),
419            storage_override.clone(),
420            fee_history_cache.clone(),
421            FEE_HISTORY_LIMIT,
422        ),
423    );
424
425    let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new(
426        task_manager.spawn_handle(),
427        storage_override.clone(),
428        50,
429        50,
430        prometheus_registry.clone(),
431    ));
432
433    let rpc_extensions_builder = {
434        let client = client.clone();
435        let network = network.clone();
436        let transaction_pool = transaction_pool.clone();
437        let rpc_config = crate::rpc::EvmTracingConfig {
438            tracing_requesters,
439            trace_filter_max_count: additional_config.evm_tracing_config.ethapi_trace_max_count,
440            trace_filter_max_block_range: additional_config
441                .evm_tracing_config
442                .trace_filter_max_block_range,
443            enable_txpool: ethapi_cmd.contains(&EthApiCmd::TxPool),
444        };
445        let sync = sync_service.clone();
446        let pubsub_notification_sinks = pubsub_notification_sinks.clone();
447
448        Box::new(move |subscription| {
449            let deps = crate::rpc::FullDeps {
450                client: client.clone(),
451                pool: transaction_pool.clone(),
452                graph: transaction_pool.clone(),
453                network: network.clone(),
454                sync: sync.clone(),
455                is_authority,
456                frontier_backend: match *frontier_backend {
457                    fc_db::Backend::KeyValue(ref b) => b.clone(),
458                    fc_db::Backend::Sql(ref b) => b.clone(),
459                },
460                filter_pool: filter_pool.clone(),
461                fee_history_limit: FEE_HISTORY_LIMIT,
462                fee_history_cache: fee_history_cache.clone(),
463                block_data_cache: block_data_cache.clone(),
464                storage_override: storage_override.clone(),
465                enable_evm_rpc: additional_config.enable_evm_rpc,
466                command_sink: None,
467            };
468
469            crate::rpc::create_full(
470                deps,
471                subscription,
472                pubsub_notification_sinks.clone(),
473                rpc_config.clone(),
474            )
475            .map_err(Into::into)
476        })
477    };
478
479    // Extract values needed after `parachain_config` is moved into `spawn_tasks`.
480    let parachain_advertise_non_global_ips = parachain_config.network.allow_non_globals_in_dht;
481    let parachain_fork_id = parachain_config
482        .chain_spec
483        .fork_id()
484        .map(ToString::to_string);
485    let parachain_public_addresses = parachain_config.network.public_addresses.clone();
486
487    // Spawn basic services.
488    sc_service::spawn_tasks(sc_service::SpawnTasksParams {
489        rpc_builder: rpc_extensions_builder,
490        client: client.clone(),
491        transaction_pool: transaction_pool.clone(),
492        task_manager: &mut task_manager,
493        config: parachain_config,
494        keystore: keystore_container.keystore(),
495        backend: backend.clone(),
496        network: network.clone(),
497        system_rpc_tx,
498        sync_service: sync_service.clone(),
499        tx_handler_controller,
500        telemetry: telemetry.as_mut(),
501        tracing_execute_block: Some(Arc::new(ParachainTracingExecuteBlock::new(client.clone()))),
502    })?;
503
504    if let Some(hwbench) = additional_config.hwbench.clone() {
505        sc_sysinfo::print_hwbench(&hwbench);
506        if is_authority {
507            warn_if_slow_hardware(&hwbench);
508        }
509
510        if let Some(ref mut telemetry) = telemetry {
511            let telemetry_handle = telemetry.handle();
512            task_manager.spawn_handle().spawn(
513                "telemetry_hwbench",
514                None,
515                sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench),
516            );
517        }
518    }
519
520    let announce_block = {
521        let sync_service = sync_service.clone();
522        Arc::new(move |hash, data| sync_service.announce_block(hash, data))
523    };
524
525    let overseer_handle = relay_chain_interface
526        .overseer_handle()
527        .map_err(|e| sc_service::Error::Application(Box::new(e)))?;
528
529    start_relay_chain_tasks(StartRelayChainTasksParams {
530        client: client.clone(),
531        announce_block: announce_block.clone(),
532        task_manager: &mut task_manager,
533        para_id,
534        relay_chain_interface: relay_chain_interface.clone(),
535        relay_chain_slot_duration: Duration::from_secs(6),
536        import_queue: import_queue_service,
537        recovery_handle: Box::new(overseer_handle.clone()),
538        sync_service: sync_service.clone(),
539        da_recovery_profile: if is_authority {
540            DARecoveryProfile::Collator
541        } else {
542            DARecoveryProfile::FullNode
543        },
544        prometheus_registry: prometheus_registry.as_ref(),
545    })?;
546
547    start_bootnode_tasks(StartBootnodeTasksParams {
548        embedded_dht_bootnode: collator_options.embedded_dht_bootnode,
549        dht_bootnode_discovery: collator_options.dht_bootnode_discovery,
550        para_id,
551        task_manager: &mut task_manager,
552        relay_chain_interface: relay_chain_interface.clone(),
553        relay_chain_fork_id,
554        relay_chain_network,
555        request_receiver: paranode_rx,
556        parachain_network: network.clone(),
557        advertise_non_global_ips: parachain_advertise_non_global_ips,
558        parachain_genesis_hash: client.chain_info().genesis_hash.as_ref().to_vec(),
559        parachain_fork_id,
560        parachain_public_addresses,
561    });
562
563    if is_authority {
564        start_aura_consensus(
565            client.clone(),
566            backend,
567            parachain_block_import,
568            block_import_handle,
569            prometheus_registry.as_ref(),
570            telemetry.map(|t| t.handle()),
571            &mut task_manager,
572            relay_chain_interface,
573            transaction_pool,
574            sync_service,
575            keystore_container.keystore(),
576            para_id,
577            collator_key.expect("Command line arguments do not allow this. qed"),
578            network.local_peer_id(),
579            additional_config,
580        )?;
581    }
582
583    Ok((task_manager, client))
584}
585
586/// Build aura import queue with fallback to relay-chain verifier.
587/// Starts with relay-chain verifier until aura becomes available.
588pub fn build_import_queue(
589    client: Arc<FullClient>,
590    block_import: ParachainBlockImport,
591    config: &Configuration,
592    telemetry_handle: Option<TelemetryHandle>,
593    task_manager: &TaskManager,
594) -> sc_consensus::DefaultImportQueue<Block> {
595    let verifier_client = client.clone();
596    // CIDP for Aura verifier - requires AuraApi for slot_duration
597    let create_aura_inherent_data_providers = move |parent_hash, _| {
598        let cidp_client = verifier_client.clone();
599        async move {
600            let slot_duration =
601                cumulus_client_consensus_aura::slot_duration_at(&*cidp_client, parent_hash)?;
602            let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
603
604            let slot =
605                sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration(
606                    *timestamp,
607                    slot_duration,
608                );
609
610            Ok((slot, timestamp))
611        }
612    };
613
614    // CIDP for relay chain verifier - minimal, only timestamp (no AuraApi required)
615    // This is used for pre-AuraApi blocks (Shiden genesis did not start with AuraApi)
616    let create_relay_inherent_data_providers = move |_parent_hash: Hash, _| async move {
617        let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
618        Ok(timestamp)
619    };
620
621    let aura_verifier = Box::new(cumulus_client_consensus_aura::build_verifier::<
622        AuraPair,
623        _,
624        _,
625        _,
626    >(cumulus_client_consensus_aura::BuildVerifierParams {
627        client: client.clone(),
628        create_inherent_data_providers: create_aura_inherent_data_providers,
629        telemetry: telemetry_handle,
630    }));
631
632    let relay_chain_verifier = Box::new(RelayChainVerifier::new(
633        client.clone(),
634        create_relay_inherent_data_providers,
635    )) as Box<_>;
636
637    let verifier = Verifier {
638        client,
639        relay_chain_verifier,
640        aura_verifier,
641    };
642
643    let registry = config.prometheus_registry();
644    let spawner = task_manager.spawn_essential_handle();
645
646    BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry)
647}
648
649/// Start collating with the `shell` runtime while waiting for an upgrade to an Aura compatible runtime.
650fn start_aura_consensus(
651    client: Arc<FullClient>,
652    backend: Arc<TFullBackend<Block>>,
653    block_import: ParachainBlockImport,
654    block_import_handle: SlotBasedBlockImportHandle<Block>,
655    prometheus_registry: Option<&Registry>,
656    telemetry: Option<TelemetryHandle>,
657    task_manager: &TaskManager,
658    relay_chain_interface: Arc<dyn RelayChainInterface>,
659    transaction_pool: Arc<sc_transaction_pool::TransactionPoolHandle<Block, FullClient>>,
660    sync_oracle: Arc<SyncingService<Block>>,
661    keystore: KeystorePtr,
662    para_id: ParaId,
663    collator_key: CollatorPair,
664    collator_peer_id: PeerId,
665    additional_config: AdditionalConfig,
666) -> Result<(), sc_service::Error> {
667    let mut proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording(
668        task_manager.spawn_handle(),
669        client.clone(),
670        transaction_pool,
671        prometheus_registry,
672        telemetry,
673    );
674
675    proposer_factory.set_default_block_size_limit(additional_config.proposer_block_size_limit);
676    proposer_factory.set_soft_deadline(Percent::from_percent(
677        additional_config.proposer_soft_deadline_percent,
678    ));
679
680    let announce_block = {
681        let sync_service = sync_oracle.clone();
682        Arc::new(move |hash, data| sync_service.announce_block(hash, data))
683    };
684
685    let collator_service = cumulus_client_collator::service::CollatorService::new(
686        client.clone(),
687        Arc::new(task_manager.spawn_handle()),
688        announce_block,
689        client.clone(),
690    );
691
692    let params = AuraParams {
693        create_inherent_data_providers: move |_, ()| async move { Ok(()) },
694        block_import,
695        para_client: client.clone(),
696        para_backend: backend,
697        relay_client: relay_chain_interface.clone(),
698        code_hash_provider: {
699            let client = client.clone();
700            move |block_hash| {
701                client
702                    .code_at(block_hash)
703                    .ok()
704                    .map(|c| ValidationCode::from(c).hash())
705            }
706        },
707        keystore,
708        collator_key,
709        collator_peer_id,
710        para_id,
711        slot_offset: Duration::from_secs(1),
712        relay_chain_slot_duration: Duration::from_secs(6),
713        proposer: proposer_factory,
714        collator_service,
715        authoring_duration: Duration::from_millis(2000),
716        reinitialize: false,
717        block_import_handle,
718        spawner: task_manager.spawn_essential_handle(),
719        // If necessary, AdditionalConfig CLI params could be extend to make it configurable.
720        // However, it will be removed once https://github.com/paritytech/polkadot-sdk/issues/6020 is fixed.
721        max_pov_percentage: None, // default is 85%
722        export_pov: None,
723    };
724
725    let fut = async move {
726        wait_for_aura(client).await;
727        aura::run::<Block, AuraPair, _, _, _, _, _, _, _, _, _>(params);
728    };
729
730    task_manager.spawn_handle().spawn("aura", None, fut);
731    Ok(())
732}
733
734/// Wait for the Aura runtime API to appear on chain.
735/// This is useful for chains that started out without Aura. Components that
736/// are depending on Aura functionality will wait until Aura appears in the runtime.
737async fn wait_for_aura(client: Arc<FullClient>) {
738    let finalized_hash = client.chain_info().finalized_hash;
739    if client
740        .runtime_api()
741        .has_api::<dyn AuraApi<Block, AuraId>>(finalized_hash)
742        .unwrap_or_default()
743    {
744        return;
745    };
746
747    let mut stream = client.finality_notification_stream();
748    while let Some(notification) = stream.next().await {
749        if client
750            .runtime_api()
751            .has_api::<dyn AuraApi<Block, AuraId>>(notification.hash)
752            .unwrap_or_default()
753        {
754            return;
755        }
756    }
757}
758
759/// Checks that the hardware meets the requirements and print a warning otherwise.
760fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) {
761    // Polkadot para-chains should generally use these requirements to ensure that the relay-chain
762    // will not take longer than expected to import its blocks.
763    if let Err(err) =
764        frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench, false)
765    {
766        log::warn!(
767            "⚠️  The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\
768            https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware",
769            err
770        );
771    }
772}
773
774/// Start a parachain node.
775pub async fn start_node(
776    parachain_config: Configuration,
777    polkadot_config: Configuration,
778    collator_options: CollatorOptions,
779    para_id: ParaId,
780    additional_config: AdditionalConfig,
781) -> sc_service::error::Result<(TaskManager, Arc<FullClient>)> {
782    match parachain_config.network.network_backend {
783        NetworkBackendType::Libp2p => {
784            start_node_impl::<sc_network::NetworkWorker<_, _>>(
785                parachain_config,
786                polkadot_config,
787                collator_options,
788                para_id,
789                additional_config,
790            )
791            .await
792        }
793        NetworkBackendType::Litep2p => {
794            start_node_impl::<sc_network::Litep2pNetworkBackend>(
795                parachain_config,
796                polkadot_config,
797                collator_options,
798                para_id,
799                additional_config,
800            )
801            .await
802        }
803    }
804}