astar_collator/local/
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//! Local Service and ServiceFactory implementation. Specialized wrapper over substrate service.
20
21pub use crate::parachain::fake_runtime_api::RuntimeApi;
22use crate::{
23    evm_tracing_types::{EthApi as EthApiCmd, FrontierConfig},
24    rpc::tracing,
25};
26use cumulus_client_parachain_inherent::MockXcmConfig;
27use cumulus_primitives_aura::Slot;
28use cumulus_primitives_core::{
29    relay_chain,
30    relay_chain::{well_known_keys, AsyncBackingParams, HeadData, UpgradeGoAhead},
31    AbridgedHostConfiguration, CollectCollationInfo, InboundHrmpMessage, ParaId,
32    RelayParentOffsetApi,
33};
34use cumulus_primitives_parachain_inherent::{MessageQueueChain, ParachainInherentData};
35use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
36use fc_consensus::FrontierBlockImport;
37use fc_rpc_core::types::{FeeHistoryCache, FilterPool};
38use fc_storage::StorageOverrideHandler;
39use futures::{FutureExt, StreamExt};
40use parity_scale_codec::Encode;
41use polkadot_core_primitives::InboundDownwardMessage;
42use polkadot_primitives::PersistedValidationData;
43use sc_client_api::{Backend, BlockchainEvents};
44use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY};
45use sc_network::NetworkBackend;
46use sc_service::{error::Error as ServiceError, Configuration, TaskManager};
47use sc_telemetry::{Telemetry, TelemetryWorker};
48use sc_transaction_pool_api::OffchainTransactionPoolFactory;
49use sp_api::ProvideRuntimeApi;
50use sp_blockchain::HeaderBackend;
51use sp_inherents::{InherentData, InherentDataProvider};
52use sp_runtime::traits::{Block as BlockT, Header as HeaderT, UniqueSaturatedInto};
53use std::{collections::BTreeMap, marker::PhantomData, ops::Sub, sync::Arc, time::Duration};
54
55use astar_primitives::*;
56
57/// Local pending inherent provider for ETH pending RPC in dev mode.
58pub struct LocalPendingInherentDataProvider<B, C> {
59    client: Arc<C>,
60    para_id: ParaId,
61    phantom_data: PhantomData<B>,
62}
63
64const RELAY_CHAIN_SLOT_DURATION_MILLIS: u64 = 6000;
65
66/// Inherent data provider that supplies mocked validation data.
67/// TODO: Use it from PolkadotSDK again after stable2603 uplift
68#[derive(Default)]
69pub struct MockValidationDataInherentDataProvider<R = ()> {
70    /// The current block number of the local block chain (the parachain).
71    pub current_para_block: u32,
72    /// The parachain ID of the parachain for that the inherent data is created.
73    pub para_id: ParaId,
74    /// The current block head data of the local block chain (the parachain).
75    pub current_para_block_head: Option<cumulus_primitives_core::relay_chain::HeadData>,
76    /// The relay block in which this parachain appeared to start. This will be the relay block
77    /// number in para block #P1.
78    pub relay_offset: u32,
79    /// The relay parent offset that determines how many relay parent descendants are required.
80    pub relay_parent_offset: u32,
81    /// The number of relay blocks that elapses between each parablock. Probably set this to 1 or 2
82    /// to simulate optimistic or realistic relay chain behavior.
83    pub relay_blocks_per_para_block: u32,
84    /// Number of parachain blocks per relay chain epoch
85    /// Mock epoch is computed by dividing `current_para_block` by this value.
86    pub para_blocks_per_relay_epoch: u32,
87    /// Function to mock BABE one epoch ago randomness.
88    pub relay_randomness_config: R,
89    /// XCM messages and associated configuration information.
90    pub xcm_config: MockXcmConfig,
91    /// Inbound downward XCM messages to be injected into the block.
92    pub raw_downward_messages: Vec<Vec<u8>>,
93    /// Inbound Horizontal messages sorted by channel.
94    pub raw_horizontal_messages: Vec<(ParaId, Vec<u8>)>,
95    /// Additional key-value pairs that should be injected.
96    pub additional_key_values: Option<Vec<(Vec<u8>, Vec<u8>)>>,
97    /// Whether upgrade go ahead should be set.
98    pub upgrade_go_ahead: Option<UpgradeGoAhead>,
99}
100
101/// Something that can generate randomness.
102pub trait GenerateRandomness<I> {
103    /// Generate the randomness using the given `input`.
104    fn generate_randomness(&self, input: I) -> relay_chain::Hash;
105}
106
107impl GenerateRandomness<u64> for () {
108    /// Default implementation uses relay epoch as randomness value
109    /// A more seemingly random implementation may hash the relay epoch instead
110    fn generate_randomness(&self, input: u64) -> relay_chain::Hash {
111        let mut mock_randomness: [u8; 32] = [0u8; 32];
112        mock_randomness[..8].copy_from_slice(&input.to_be_bytes());
113        mock_randomness.into()
114    }
115}
116
117#[async_trait::async_trait]
118impl<R: Send + Sync + GenerateRandomness<u64>> InherentDataProvider
119    for MockValidationDataInherentDataProvider<R>
120{
121    async fn provide_inherent_data(
122        &self,
123        inherent_data: &mut InherentData,
124    ) -> Result<(), sp_inherents::Error> {
125        // Use the "sproof" (spoof proof) builder to build valid mock state root and proof.
126        let mut sproof_builder = RelayStateSproofBuilder {
127            para_id: self.para_id,
128            ..Default::default()
129        };
130
131        // Calculate the mocked relay block based on the current para block
132        let relay_parent_number =
133            self.relay_offset + self.relay_blocks_per_para_block * self.current_para_block;
134        sproof_builder.current_slot = Slot::from(relay_parent_number as u64);
135
136        sproof_builder.upgrade_go_ahead = self.upgrade_go_ahead;
137        // Process the downward messages and set up the correct head
138        let mut downward_messages = Vec::new();
139        let mut dmq_mqc = MessageQueueChain::new(self.xcm_config.starting_dmq_mqc_head);
140        for msg in &self.raw_downward_messages {
141            let wrapped = InboundDownwardMessage {
142                sent_at: relay_parent_number,
143                msg: msg.clone(),
144            };
145
146            dmq_mqc.extend_downward(&wrapped);
147            downward_messages.push(wrapped);
148        }
149        sproof_builder.dmq_mqc_head = Some(dmq_mqc.head());
150
151        // Process the hrmp messages and set up the correct heads
152        // Begin by collecting them into a Map
153        let mut horizontal_messages = BTreeMap::<ParaId, Vec<InboundHrmpMessage>>::new();
154        for (para_id, msg) in &self.raw_horizontal_messages {
155            let wrapped = InboundHrmpMessage {
156                sent_at: relay_parent_number,
157                data: msg.clone(),
158            };
159
160            horizontal_messages
161                .entry(*para_id)
162                .or_default()
163                .push(wrapped);
164        }
165
166        // Now iterate again, updating the heads as we go
167        for (para_id, messages) in &horizontal_messages {
168            let mut channel_mqc = MessageQueueChain::new(
169                *self
170                    .xcm_config
171                    .starting_hrmp_mqc_heads
172                    .get(para_id)
173                    .unwrap_or(&relay_chain::Hash::default()),
174            );
175            for message in messages {
176                channel_mqc.extend_hrmp(message);
177            }
178            sproof_builder.upsert_inbound_channel(*para_id).mqc_head = Some(channel_mqc.head());
179        }
180
181        // Epoch is set equal to current para block / blocks per epoch
182        sproof_builder.current_epoch = if self.para_blocks_per_relay_epoch == 0 {
183            // do not divide by 0 => set epoch to para block number
184            self.current_para_block.into()
185        } else {
186            (self.current_para_block / self.para_blocks_per_relay_epoch).into()
187        };
188        // Randomness is set by randomness generator
189        sproof_builder.randomness = self
190            .relay_randomness_config
191            .generate_randomness(self.current_para_block.into());
192
193        if let Some(key_values) = &self.additional_key_values {
194            sproof_builder.additional_key_values = key_values.clone()
195        }
196
197        // Inject current para block head, if any
198        sproof_builder.included_para_head = self.current_para_block_head.clone();
199
200        let (relay_parent_storage_root, proof, relay_parent_descendants) =
201            sproof_builder.into_state_root_proof_and_descendants(self.relay_parent_offset.into());
202        let parachain_inherent_data = ParachainInherentData {
203            validation_data: PersistedValidationData {
204                parent_head: Default::default(),
205                relay_parent_storage_root,
206                relay_parent_number,
207                max_pov_size: Default::default(),
208            },
209            downward_messages,
210            horizontal_messages,
211            relay_chain_state: proof,
212            relay_parent_descendants,
213            collator_peer_id: None,
214        };
215
216        parachain_inherent_data
217            .provide_inherent_data(inherent_data)
218            .await
219    }
220
221    // Copied from the real implementation
222    async fn try_handle_error(
223        &self,
224        _: &sp_inherents::InherentIdentifier,
225        _: &[u8],
226    ) -> Option<Result<(), sp_inherents::Error>> {
227        None
228    }
229}
230
231fn build_local_mock_inherent_data(
232    para_id: ParaId,
233    current_para_block: u32,
234    current_para_block_head: Option<HeadData>,
235    relay_blocks_per_para_block: u32,
236    relay_slot: u64,
237    relay_parent_offset: u32,
238    upgrade_go_ahead: Option<UpgradeGoAhead>,
239) -> (
240    sp_timestamp::InherentDataProvider,
241    MockValidationDataInherentDataProvider<()>,
242) {
243    let relay_offset = relay_parent_offset.saturating_add(
244        (relay_slot as u32)
245            .saturating_sub(relay_blocks_per_para_block.saturating_mul(current_para_block)),
246    );
247
248    let local_host_config = AbridgedHostConfiguration {
249        max_code_size: 16 * 1024 * 1024, // 16 MiB (local dev only)
250        max_head_data_size: 1024 * 1024,
251        max_upward_queue_count: 8,
252        max_upward_queue_size: 1024,
253        max_upward_message_size: 256,
254        max_upward_message_num_per_candidate: 5,
255        hrmp_max_message_num_per_candidate: 5,
256        validation_upgrade_cooldown: 6,
257        validation_upgrade_delay: 6,
258        async_backing_params: AsyncBackingParams {
259            allowed_ancestry_len: 0,
260            max_candidate_depth: 0,
261        },
262    };
263
264    let mocked_parachain = MockValidationDataInherentDataProvider::<()> {
265        current_para_block,
266        para_id,
267        current_para_block_head,
268        relay_blocks_per_para_block,
269        relay_offset,
270        relay_parent_offset,
271        para_blocks_per_relay_epoch: 10,
272        upgrade_go_ahead,
273        additional_key_values: Some(vec![(
274            well_known_keys::ACTIVE_CONFIG.to_vec(),
275            local_host_config.encode(),
276        )]),
277        ..Default::default()
278    };
279
280    let timestamp = relay_slot
281        .saturating_add(u64::from(relay_parent_offset))
282        .saturating_mul(RELAY_CHAIN_SLOT_DURATION_MILLIS);
283    let timestamp_provider = sp_timestamp::InherentDataProvider::new(timestamp.into());
284
285    (timestamp_provider, mocked_parachain)
286}
287
288impl<B, C> LocalPendingInherentDataProvider<B, C> {
289    /// Creates a new instance with the given client and parachain ID.
290    pub fn new(client: Arc<C>, para_id: ParaId) -> Self {
291        Self {
292            client,
293            para_id,
294            phantom_data: Default::default(),
295        }
296    }
297}
298
299#[async_trait::async_trait]
300impl<B, C> sp_inherents::CreateInherentDataProviders<B, ()>
301    for LocalPendingInherentDataProvider<B, C>
302where
303    B: BlockT,
304    C: ProvideRuntimeApi<B> + HeaderBackend<B> + Send + Sync,
305    C::Api: RelayParentOffsetApi<B>,
306{
307    type InherentDataProviders = (
308        sp_timestamp::InherentDataProvider,
309        MockValidationDataInherentDataProvider<()>,
310    );
311
312    async fn create_inherent_data_providers(
313        &self,
314        parent: B::Hash,
315        _extra_args: (),
316    ) -> Result<Self::InherentDataProviders, Box<dyn std::error::Error + Send + Sync>> {
317        let relay_slot = std::time::SystemTime::now()
318            .duration_since(std::time::UNIX_EPOCH)
319            .expect("Current time is always after UNIX_EPOCH; qed")
320            .as_millis() as u64
321            / RELAY_CHAIN_SLOT_DURATION_MILLIS;
322
323        let current_para_block = self
324            .client
325            .header(parent)?
326            .map(|header| {
327                UniqueSaturatedInto::<u32>::unique_saturated_into(*header.number())
328                    .saturating_add(1)
329            })
330            .unwrap_or(1);
331
332        let current_para_block_head = self
333            .client
334            .header(parent)?
335            .map(|header| header.encode().into());
336
337        let relay_parent_offset = self.client.runtime_api().relay_parent_offset(parent)?;
338
339        let (timestamp_provider, mocked_parachain) = build_local_mock_inherent_data(
340            self.para_id,
341            current_para_block,
342            current_para_block_head,
343            1,
344            relay_slot,
345            relay_parent_offset,
346            None,
347        );
348
349        Ok((timestamp_provider, mocked_parachain))
350    }
351}
352
353/// Parachain host functions
354#[cfg(feature = "runtime-benchmarks")]
355pub type HostFunctions = (
356    frame_benchmarking::benchmarking::HostFunctions,
357    cumulus_client_service::ParachainHostFunctions,
358    moonbeam_primitives_ext::moonbeam_ext::HostFunctions,
359);
360
361/// Parachain host functions
362#[cfg(not(feature = "runtime-benchmarks"))]
363pub type HostFunctions = (
364    cumulus_client_service::ParachainHostFunctions,
365    moonbeam_primitives_ext::moonbeam_ext::HostFunctions,
366);
367
368type ParachainExecutor = WasmExecutor<HostFunctions>;
369
370type FullClient = sc_service::TFullClient<Block, RuntimeApi, ParachainExecutor>;
371type FullBackend = sc_service::TFullBackend<Block>;
372type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
373
374/// Build a partial chain component config
375pub fn new_partial(
376    config: &Configuration,
377    evm_tracing_config: &FrontierConfig,
378) -> Result<
379    sc_service::PartialComponents<
380        FullClient,
381        FullBackend,
382        FullSelectChain,
383        sc_consensus::DefaultImportQueue<Block>,
384        sc_transaction_pool::TransactionPoolHandle<Block, FullClient>,
385        (
386            FrontierBlockImport<Block, Arc<FullClient>, FullClient>,
387            Option<Telemetry>,
388            Arc<fc_db::Backend<Block, FullClient>>,
389        ),
390    >,
391    ServiceError,
392> {
393    let telemetry = config
394        .telemetry_endpoints
395        .clone()
396        .filter(|x| !x.is_empty())
397        .map(|endpoints| -> Result<_, sc_telemetry::Error> {
398            let worker = TelemetryWorker::new(16)?;
399            let telemetry = worker.handle().new_telemetry(endpoints);
400            Ok((worker, telemetry))
401        })
402        .transpose()?;
403
404    let heap_pages = config
405        .executor
406        .default_heap_pages
407        .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static {
408            extra_pages: h as _,
409        });
410
411    let executor = ParachainExecutor::builder()
412        .with_execution_method(config.executor.wasm_method)
413        .with_onchain_heap_alloc_strategy(heap_pages)
414        .with_offchain_heap_alloc_strategy(heap_pages)
415        .with_max_runtime_instances(config.executor.max_runtime_instances)
416        .with_runtime_cache_size(config.executor.runtime_cache_size)
417        .build();
418
419    let (client, backend, keystore_container, task_manager) =
420        sc_service::new_full_parts_record_import::<Block, RuntimeApi, _>(
421            config,
422            telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
423            executor,
424            true,
425            Default::default(),
426        )?;
427    let client = Arc::new(client);
428    let telemetry = telemetry.map(|(worker, telemetry)| {
429        task_manager
430            .spawn_handle()
431            .spawn("telemetry", None, worker.run());
432        telemetry
433    });
434    let select_chain = sc_consensus::LongestChain::new(backend.clone());
435    let transaction_pool = sc_transaction_pool::Builder::new(
436        task_manager.spawn_essential_handle(),
437        client.clone(),
438        config.role.is_authority().into(),
439    )
440    .with_options(config.transaction_pool.clone())
441    .with_prometheus(config.prometheus_registry())
442    .build();
443    let frontier_backend = Arc::new(crate::rpc::open_frontier_backend(
444        client.clone(),
445        config,
446        evm_tracing_config,
447    )?);
448    let frontier_block_import = FrontierBlockImport::new(client.clone(), client.clone());
449
450    let import_queue = sc_consensus_manual_seal::import_queue(
451        Box::new(client.clone()),
452        &task_manager.spawn_essential_handle(),
453        config.prometheus_registry(),
454    );
455
456    Ok(sc_service::PartialComponents {
457        client,
458        backend,
459        task_manager,
460        import_queue,
461        keystore_container,
462        select_chain,
463        transaction_pool: transaction_pool.into(),
464        other: (frontier_block_import, telemetry, frontier_backend),
465    })
466}
467
468/// Builds a new local development service (parachain-oriented).
469pub fn start_node<N>(
470    mut config: Configuration,
471    evm_tracing_config: FrontierConfig,
472) -> Result<TaskManager, ServiceError>
473where
474    N: NetworkBackend<Block, <Block as BlockT>::Hash>,
475{
476    let sc_service::PartialComponents {
477        client,
478        backend,
479        mut task_manager,
480        import_queue,
481        keystore_container,
482        select_chain,
483        transaction_pool,
484        other: (block_import, mut telemetry, frontier_backend),
485    } = new_partial(&config, &evm_tracing_config)?;
486
487    // Dev node: no peers
488    config.network.default_peers_set.in_peers = 0;
489    config.network.default_peers_set.out_peers = 0;
490
491    let net_config = sc_network::config::FullNetworkConfiguration::<_, _, N>::new(
492        &config.network,
493        config.prometheus_registry().cloned(),
494    );
495
496    let metrics = N::register_notification_metrics(
497        config.prometheus_config.as_ref().map(|cfg| &cfg.registry),
498    );
499    let (network, system_rpc_tx, tx_handler_controller, sync_service) =
500        sc_service::build_network(sc_service::BuildNetworkParams {
501            config: &config,
502            net_config,
503            client: client.clone(),
504            transaction_pool: transaction_pool.clone(),
505            spawn_handle: task_manager.spawn_handle(),
506            spawn_essential_handle: task_manager.spawn_essential_handle(),
507            import_queue,
508            block_announce_validator_builder: None,
509            warp_sync_config: None,
510            block_relay: None,
511            metrics,
512        })?;
513
514    if config.offchain_worker.enabled {
515        task_manager.spawn_handle().spawn(
516            "offchain-workers-runner",
517            "offchain-work",
518            sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions {
519                runtime_api_provider: client.clone(),
520                keystore: Some(keystore_container.keystore()),
521                offchain_db: backend.offchain_storage(),
522                transaction_pool: Some(OffchainTransactionPoolFactory::new(
523                    transaction_pool.clone(),
524                )),
525                network_provider: Arc::new(network.clone()),
526                is_validator: config.role.is_authority(),
527                enable_http_requests: true,
528                custom_extensions: move |_| vec![],
529            })?
530            .run(client.clone(), task_manager.spawn_handle())
531            .boxed(),
532        );
533    }
534
535    let filter_pool: FilterPool = Arc::new(std::sync::Mutex::new(BTreeMap::new()));
536    let fee_history_cache: FeeHistoryCache = Arc::new(std::sync::Mutex::new(BTreeMap::new()));
537    let storage_override = Arc::new(StorageOverrideHandler::new(client.clone()));
538
539    // Sinks for pubsub notifications.
540    // Everytime a new subscription is created, a new mpsc channel is added to the sink pool.
541    // 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.
542    // This way we avoid race conditions when using native substrate block import notification stream.
543    let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks<
544        fc_mapping_sync::EthereumBlockNotification<Block>,
545    > = Default::default();
546    let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks);
547
548    let ethapi_cmd = evm_tracing_config.ethapi.clone();
549
550    let tracing_requesters =
551        if ethapi_cmd.contains(&EthApiCmd::Debug) || ethapi_cmd.contains(&EthApiCmd::Trace) {
552            tracing::spawn_tracing_tasks(
553                &evm_tracing_config,
554                config.prometheus_registry().cloned(),
555                tracing::SpawnTasksParams {
556                    task_manager: &task_manager,
557                    client: client.clone(),
558                    substrate_backend: backend.clone(),
559                    frontier_backend: frontier_backend.clone(),
560                    storage_override: storage_override.clone(),
561                },
562            )
563        } else {
564            tracing::RpcRequesters {
565                debug: None,
566                trace: None,
567            }
568        };
569
570    // Frontier offchain DB task. Essential.
571    // Maps emulated ethereum data to substrate native data.
572    match frontier_backend.as_ref() {
573        fc_db::Backend::KeyValue(ref b) => {
574            task_manager.spawn_essential_handle().spawn(
575                "frontier-mapping-sync-worker",
576                Some("frontier"),
577                fc_mapping_sync::kv::MappingSyncWorker::new(
578                    client.import_notification_stream(),
579                    Duration::new(6, 0),
580                    client.clone(),
581                    backend.clone(),
582                    storage_override.clone(),
583                    b.clone(),
584                    3,
585                    0,
586                    None,
587                    fc_mapping_sync::SyncStrategy::Parachain,
588                    sync_service.clone(),
589                    pubsub_notification_sinks.clone(),
590                )
591                .for_each(|()| futures::future::ready(())),
592            );
593        }
594        fc_db::Backend::Sql(ref b) => {
595            task_manager.spawn_essential_handle().spawn_blocking(
596                "frontier-mapping-sync-worker",
597                Some("frontier"),
598                fc_mapping_sync::sql::SyncWorker::run(
599                    client.clone(),
600                    backend.clone(),
601                    b.clone(),
602                    client.import_notification_stream(),
603                    fc_mapping_sync::sql::SyncWorkerConfig {
604                        read_notification_timeout: Duration::from_secs(10),
605                        check_indexed_blocks_interval: Duration::from_secs(60),
606                    },
607                    fc_mapping_sync::SyncStrategy::Parachain,
608                    sync_service.clone(),
609                    pubsub_notification_sinks.clone(),
610                ),
611            );
612        }
613    }
614
615    // Frontier `EthFilterApi` maintenance. Manages the pool of user-created Filters.
616    // Each filter is allowed to stay in the pool for 100 blocks.
617    const FILTER_RETAIN_THRESHOLD: u64 = 100;
618    task_manager.spawn_essential_handle().spawn(
619        "frontier-filter-pool",
620        Some("frontier"),
621        fc_rpc::EthTask::filter_pool_task(
622            client.clone(),
623            filter_pool.clone(),
624            FILTER_RETAIN_THRESHOLD,
625        ),
626    );
627
628    const FEE_HISTORY_LIMIT: u64 = 2048;
629    task_manager.spawn_essential_handle().spawn(
630        "frontier-fee-history",
631        Some("frontier"),
632        fc_rpc::EthTask::fee_history_task(
633            client.clone(),
634            storage_override.clone(),
635            fee_history_cache.clone(),
636            FEE_HISTORY_LIMIT,
637        ),
638    );
639
640    let role = config.role.clone();
641    let prometheus_registry = config.prometheus_registry().cloned();
642    let is_authority = config.role.is_authority();
643
644    let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new(
645        task_manager.spawn_handle(),
646        storage_override.clone(),
647        50,
648        50,
649        prometheus_registry.clone(),
650    ));
651
652    // Channel for the rpc handler to communicate with the authorship task.
653    let (command_sink, commands_stream) = futures::channel::mpsc::channel(1024);
654    let local_para_id = ParaId::from(
655        crate::parachain::chain_spec::Extensions::try_get(&*config.chain_spec)
656            .map(|e| e.para_id)
657            .unwrap_or(2000),
658    );
659
660    let rpc_extensions_builder = {
661        let client = client.clone();
662        let network = network.clone();
663        let transaction_pool = transaction_pool.clone();
664        let sync = sync_service.clone();
665        let pubsub_notification_sinks = pubsub_notification_sinks.clone();
666
667        Box::new(move |subscription| {
668            let deps = crate::rpc::FullDeps {
669                client: client.clone(),
670                pool: transaction_pool.clone(),
671                graph: transaction_pool.clone(),
672                network: network.clone(),
673                sync: sync.clone(),
674                is_authority,
675                frontier_backend: match *frontier_backend {
676                    fc_db::Backend::KeyValue(ref b) => b.clone(),
677                    fc_db::Backend::Sql(ref b) => b.clone(),
678                },
679                filter_pool: filter_pool.clone(),
680                fee_history_limit: FEE_HISTORY_LIMIT,
681                fee_history_cache: fee_history_cache.clone(),
682                block_data_cache: block_data_cache.clone(),
683                storage_override: storage_override.clone(),
684                enable_evm_rpc: true, // enable EVM RPC for dev node by default
685                command_sink: Some(command_sink.clone()),
686            };
687
688            crate::rpc::create_full_local_dev(
689                deps,
690                subscription,
691                pubsub_notification_sinks.clone(),
692                local_para_id,
693                crate::rpc::EvmTracingConfig {
694                    tracing_requesters: tracing_requesters.clone(),
695                    trace_filter_max_count: evm_tracing_config.ethapi_trace_max_count,
696                    trace_filter_max_block_range: evm_tracing_config.trace_filter_max_block_range,
697                    enable_txpool: ethapi_cmd.contains(&EthApiCmd::TxPool),
698                },
699            )
700            .map_err::<ServiceError, _>(Into::into)
701        })
702    };
703
704    let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams {
705        network: network.clone(),
706        client: client.clone(),
707        keystore: keystore_container.keystore(),
708        task_manager: &mut task_manager,
709        transaction_pool: transaction_pool.clone(),
710        rpc_builder: rpc_extensions_builder,
711        backend,
712        system_rpc_tx,
713        tx_handler_controller,
714        sync_service: sync_service.clone(),
715        config,
716        telemetry: telemetry.as_mut(),
717        tracing_execute_block: None,
718    })?;
719
720    if role.is_authority() {
721        let proposer_factory = sc_basic_authorship::ProposerFactory::new(
722            task_manager.spawn_handle(),
723            client.clone(),
724            transaction_pool.clone(),
725            prometheus_registry.as_ref(),
726            telemetry.as_ref().map(|x| x.handle()),
727        );
728
729        let slot_duration = sc_consensus_aura::slot_duration(&*client)?;
730
731        let para_id = local_para_id;
732        let initial_relay_slot = std::time::SystemTime::now()
733            .duration_since(std::time::UNIX_EPOCH)
734            .expect("Current time is always after UNIX_EPOCH; qed")
735            .sub(Duration::from_secs(2 * 60 * 60))
736            .as_millis() as u64
737            / RELAY_CHAIN_SLOT_DURATION_MILLIS;
738
739        let aura =
740            sc_consensus_manual_seal::run_manual_seal(sc_consensus_manual_seal::ManualSealParams {
741                block_import,
742                env: proposer_factory,
743                client: client.clone(),
744                pool: transaction_pool.clone(),
745                commands_stream,
746                select_chain,
747                consensus_data_provider: Some(Box::new(
748                    sc_consensus_manual_seal::consensus::aura::AuraConsensusDataProvider::new(
749                        client.clone(),
750                    ),
751                )),
752                create_inherent_data_providers: move |parent_hash, ()| {
753                    let client = client.clone();
754                    async move {
755                        let current_para_head = client
756                            .header(parent_hash)
757                            .expect("Header lookup should succeed")
758                            .expect("Header passed in as parent should be present in backend.");
759
760                        let should_send_go_ahead = client
761                            .runtime_api()
762                            .collect_collation_info(parent_hash, &current_para_head)
763                            .map(|info| info.new_validation_code.is_some())
764                            .unwrap_or_default();
765
766                        let current_para_block = UniqueSaturatedInto::<u32>::unique_saturated_into(
767                            *current_para_head.number(),
768                        ) + 1;
769
770                        let relay_blocks_per_para_block =
771                            (slot_duration.as_millis() / RELAY_CHAIN_SLOT_DURATION_MILLIS).max(1)
772                                as u32;
773                        let current_para_block_u64 = u64::from(current_para_block);
774                        let relay_blocks_per_para_block_u64 =
775                            u64::from(relay_blocks_per_para_block);
776                        let target_relay_slot = initial_relay_slot.saturating_add(
777                            current_para_block_u64.saturating_mul(relay_blocks_per_para_block_u64),
778                        );
779
780                        let current_para_block_head = Some(current_para_head.encode().into());
781                        let relay_parent_offset =
782                            client.runtime_api().relay_parent_offset(parent_hash)?;
783
784                        let (timestamp_provider, mocked_parachain) = build_local_mock_inherent_data(
785                            para_id,
786                            current_para_block,
787                            current_para_block_head,
788                            relay_blocks_per_para_block,
789                            target_relay_slot,
790                            relay_parent_offset,
791                            should_send_go_ahead.then_some(UpgradeGoAhead::GoAhead),
792                        );
793
794                        Ok((timestamp_provider, mocked_parachain))
795                    }
796                },
797            });
798
799        task_manager
800            .spawn_essential_handle()
801            .spawn_blocking("aura", Some("block-authoring"), aura);
802    }
803
804    Ok(task_manager)
805}