1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! A collection of node-specific RPC methods.
18
//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer
19
//! used by Substrate nodes. This file extends those RPC definitions with
20
//! capabilities that are specific to this project's runtime configuration.
21

            
22
#![warn(missing_docs)]
23

            
24
pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor};
25

            
26
use {
27
    container_chain_template_frontier_runtime::{opaque::Block, AccountId, Hash, Index},
28
    core::marker::PhantomData,
29
    cumulus_client_parachain_inherent::ParachainInherentData,
30
    cumulus_primitives_core::{ParaId, PersistedValidationData},
31
    cumulus_test_relay_sproof_builder::RelayStateSproofBuilder,
32
    fc_rpc::{EthTask, TxPool},
33
    fc_rpc_core::TxPoolApiServer,
34
    fc_storage::StorageOverride,
35
    fp_rpc::EthereumRuntimeRPCApi,
36
    frame_support::CloneNoBound,
37
    futures::StreamExt,
38
    jsonrpsee::RpcModule,
39
    manual_xcm_rpc::{ManualXcm, ManualXcmApiServer},
40
    sc_client_api::{
41
        backend::{Backend, StateBackend},
42
        client::BlockchainEvents,
43
        AuxStore, BlockOf, StorageProvider,
44
    },
45
    sc_consensus_manual_seal::rpc::{EngineCommand, ManualSeal, ManualSealApiServer},
46
    sc_network_sync::SyncingService,
47
    sc_service::TaskManager,
48
    sc_transaction_pool::{ChainApi, Pool},
49
    sc_transaction_pool_api::TransactionPool,
50
    sp_api::{CallApiAt, ProvideRuntimeApi},
51
    sp_block_builder::BlockBuilder,
52
    sp_blockchain::{
53
        Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata,
54
    },
55
    sp_consensus_aura::SlotDuration,
56
    sp_core::H256,
57
    sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT},
58
    std::{
59
        collections::BTreeMap,
60
        sync::{Arc, Mutex},
61
        time::Duration,
62
    },
63
    tc_service_container_chain::service::{ContainerChainClient, MinimalContainerRuntimeApi},
64
};
65

            
66
pub struct DefaultEthConfig<C, BE>(std::marker::PhantomData<(C, BE)>);
67

            
68
impl<C, BE> fc_rpc::EthConfig<Block, C> for DefaultEthConfig<C, BE>
69
where
70
    C: StorageProvider<Block, BE> + Sync + Send + 'static,
71
    BE: Backend<Block> + 'static,
72
{
73
    type EstimateGasAdapter = ();
74
    type RuntimeStorageOverride =
75
        fc_rpc::frontier_backend_client::SystemAccountId20StorageOverride<Block, C, BE>;
76
}
77

            
78
mod eth;
79
pub use eth::*;
80
mod finality;
81

            
82
/// Full client dependencies.
83
pub struct FullDeps<C, P, A: ChainApi, BE> {
84
    /// The client instance to use.
85
    pub client: Arc<C>,
86
    /// Transaction pool instance.
87
    pub pool: Arc<P>,
88
    /// Graph pool instance.
89
    pub graph: Arc<Pool<A>>,
90
    /// Whether to deny unsafe calls
91
    pub deny_unsafe: DenyUnsafe,
92
    /// Network service
93
    pub network: Arc<dyn sc_network::service::traits::NetworkService>,
94
    /// Chain syncing service
95
    pub sync: Arc<SyncingService<Block>>,
96
    /// EthFilterApi pool.
97
    pub filter_pool: Option<FilterPool>,
98
    /// Frontier Backend.
99
    // TODO: log indexer?
100
    pub frontier_backend: Arc<dyn fc_api::Backend<Block>>,
101
    /// Backend.
102
    #[allow(dead_code)] // not used but keep nice type inference
103
    pub backend: Arc<BE>,
104
    /// Maximum number of logs in a query.
105
    pub max_past_logs: u32,
106
    /// Maximum fee history cache size.
107
    pub fee_history_limit: u64,
108
    /// Fee history cache.
109
    pub fee_history_cache: FeeHistoryCache,
110
    /// Ethereum data access overrides.
111
    pub overrides: Arc<dyn StorageOverride<Block>>,
112
    /// Cache for Ethereum block data.
113
    pub block_data_cache: Arc<EthBlockDataCacheTask<Block>>,
114
    /// The Node authority flag
115
    pub is_authority: bool,
116
    /// Manual seal command sink
117
    pub command_sink: Option<futures::channel::mpsc::Sender<EngineCommand<Hash>>>,
118
    /// Channels for manual xcm messages (downward, hrmp)
119
    pub xcm_senders: Option<(flume::Sender<Vec<u8>>, flume::Sender<(ParaId, Vec<u8>)>)>,
120
}
121

            
122
/// Instantiate all Full RPC extensions.
123
276
pub fn create_full<C, P, BE, A>(
124
276
    deps: FullDeps<C, P, A, BE>,
125
276
    subscription_task_executor: SubscriptionTaskExecutor,
126
276
    pubsub_notification_sinks: Arc<
127
276
        fc_mapping_sync::EthereumBlockNotificationSinks<
128
276
            fc_mapping_sync::EthereumBlockNotification<Block>,
129
276
        >,
130
276
    >,
131
276
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
132
276
where
133
276
    BE: Backend<Block> + 'static,
134
276
    BE::State: StateBackend<BlakeTwo256>,
135
276
    BE::Blockchain: BlockchainBackend<Block>,
136
276
    C: ProvideRuntimeApi<Block> + StorageProvider<Block, BE> + AuxStore,
137
276
    C: BlockchainEvents<Block>,
138
276
    C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static,
139
276
    C: CallApiAt<Block>,
140
276
    C: Send + Sync + 'static,
141
276
    A: ChainApi<Block = Block> + 'static,
142
276
    C::Api: RuntimeApiCollection,
143
276
    P: TransactionPool<Block = Block> + 'static,
144
276
{
145
    use {
146
        fc_rpc::{
147
            Eth, EthApiServer, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, Net,
148
            NetApiServer, Web3, Web3ApiServer,
149
        },
150
        finality::{FrontierFinality, FrontierFinalityApiServer},
151
        substrate_frame_rpc_system::{System, SystemApiServer},
152
    };
153

            
154
276
    let mut io = RpcModule::new(());
155
276
    let FullDeps {
156
276
        client,
157
276
        pool,
158
276
        graph,
159
276
        deny_unsafe,
160
276
        network,
161
276
        sync,
162
276
        filter_pool,
163
276
        frontier_backend,
164
276
        backend: _,
165
276
        max_past_logs,
166
276
        fee_history_limit,
167
276
        fee_history_cache,
168
276
        overrides,
169
276
        block_data_cache,
170
276
        is_authority,
171
276
        command_sink,
172
276
        xcm_senders,
173
276
    } = deps;
174
276

            
175
276
    io.merge(System::new(Arc::clone(&client), Arc::clone(&pool), deny_unsafe).into_rpc())?;
176

            
177
    // TODO: are we supporting signing?
178
276
    let signers = Vec::new();
179

            
180
    enum Never {}
181
    impl<T> fp_rpc::ConvertTransaction<T> for Never {
182
        fn convert_transaction(&self, _transaction: pallet_ethereum::Transaction) -> T {
183
            // The Never type is not instantiable, but this method requires the type to be
184
            // instantiated to be called (`&self` parameter), so if the code compiles we have the
185
            // guarantee that this function will never be called.
186
            unreachable!()
187
        }
188
    }
189
276
    let convert_transaction: Option<Never> = None;
190
276
    let authorities = vec![tc_consensus::get_aura_id_from_seed("alice")];
191
276
    let authorities_for_cdp = authorities.clone();
192
276

            
193
276
    let pending_create_inherent_data_providers = move |_, _| {
194
4
        let authorities_for_cidp = authorities.clone();
195

            
196
4
        async move {
197
4
            let mocked_authorities_noting =
198
4
                ccp_authorities_noting_inherent::MockAuthoritiesNotingInherentDataProvider {
199
4
                    current_para_block: 1000,
200
4
                    relay_offset: 1000,
201
4
                    relay_blocks_per_para_block: 2,
202
4
                    orchestrator_para_id: 1000u32.into(),
203
4
                    container_para_id: 2000u32.into(),
204
4
                    authorities: authorities_for_cidp,
205
4
                };
206
4

            
207
4
            let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
208
4
            // Create a dummy parachain inherent data provider which is required to pass
209
4
            // the checks by the para chain system. We use dummy values because in the 'pending context'
210
4
            // neither do we have access to the real values nor do we need them.
211
4
            let (relay_parent_storage_root, relay_chain_state) = RelayStateSproofBuilder {
212
4
                additional_key_values: mocked_authorities_noting.get_key_values(),
213
4
                ..Default::default()
214
4
            }
215
4
            .into_state_root_and_proof();
216
4
            let vfp = PersistedValidationData {
217
4
                // This is a hack to make `cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases`
218
4
                // happy. Relay parent number can't be bigger than u32::MAX.
219
4
                relay_parent_number: u32::MAX,
220
4
                relay_parent_storage_root,
221
4
                ..Default::default()
222
4
            };
223
4
            let parachain_inherent_data = ParachainInherentData {
224
4
                validation_data: vfp,
225
4
                relay_chain_state,
226
4
                downward_messages: Default::default(),
227
4
                horizontal_messages: Default::default(),
228
4
            };
229
4
            Ok((
230
4
                timestamp,
231
4
                parachain_inherent_data,
232
4
                mocked_authorities_noting,
233
4
            ))
234
4
        }
235
4
    };
236

            
237
276
    let pending_consensus_data_provider_frontier: Option<
238
276
        Box<(dyn fc_rpc::pending::ConsensusDataProvider<_>)>,
239
276
    > = Some(Box::new(
240
276
        tc_consensus::ContainerManualSealAuraConsensusDataProvider::new(
241
276
            SlotDuration::from_millis(container_chain_template_frontier_runtime::SLOT_DURATION),
242
276
            authorities_for_cdp,
243
276
        ),
244
276
    ));
245
276

            
246
276
    io.merge(
247
276
        Eth::<_, _, _, _, _, _, _, DefaultEthConfig<C, BE>>::new(
248
276
            Arc::clone(&client),
249
276
            Arc::clone(&pool),
250
276
            Arc::clone(&graph),
251
276
            convert_transaction,
252
276
            Arc::clone(&sync),
253
276
            signers,
254
276
            Arc::clone(&overrides),
255
276
            Arc::clone(&frontier_backend),
256
276
            is_authority,
257
276
            Arc::clone(&block_data_cache),
258
276
            fee_history_cache,
259
276
            fee_history_limit,
260
276
            10,
261
276
            None,
262
276
            pending_create_inherent_data_providers,
263
276
            pending_consensus_data_provider_frontier,
264
276
        )
265
276
        .into_rpc(),
266
276
    )?;
267

            
268
276
    let tx_pool = TxPool::new(client.clone(), graph.clone());
269
276
    if let Some(filter_pool) = filter_pool {
270
276
        io.merge(
271
276
            EthFilter::new(
272
276
                client.clone(),
273
276
                frontier_backend.clone(),
274
276
                graph,
275
276
                filter_pool,
276
276
                500_usize, // max stored filters
277
276
                max_past_logs,
278
276
                block_data_cache,
279
276
            )
280
276
            .into_rpc(),
281
276
        )?;
282
    }
283

            
284
276
    io.merge(
285
276
        Net::new(
286
276
            Arc::clone(&client),
287
276
            network,
288
276
            // Whether to format the `peer_count` response as Hex (default) or not.
289
276
            true,
290
276
        )
291
276
        .into_rpc(),
292
276
    )?;
293

            
294
276
    if let Some(command_sink) = command_sink {
295
276
        io.merge(
296
276
            // We provide the rpc handler with the sending end of the channel to allow the rpc
297
276
            // send EngineCommands to the background block authorship task.
298
276
            ManualSeal::new(command_sink).into_rpc(),
299
276
        )?;
300
    };
301

            
302
276
    io.merge(Web3::new(Arc::clone(&client)).into_rpc())?;
303
276
    io.merge(
304
276
        EthPubSub::new(
305
276
            pool,
306
276
            Arc::clone(&client),
307
276
            sync,
308
276
            subscription_task_executor,
309
276
            overrides,
310
276
            pubsub_notification_sinks,
311
276
        )
312
276
        .into_rpc(),
313
276
    )?;
314
276
    io.merge(tx_pool.into_rpc())?;
315

            
316
276
    if let Some((downward_message_channel, hrmp_message_channel)) = xcm_senders {
317
276
        io.merge(
318
276
            ManualXcm {
319
276
                downward_message_channel,
320
276
                hrmp_message_channel,
321
276
            }
322
276
            .into_rpc(),
323
276
        )?;
324
    }
325

            
326
276
    io.merge(FrontierFinality::new(client.clone(), frontier_backend.clone()).into_rpc())?;
327

            
328
276
    Ok(io)
329
276
}
330

            
331
pub struct SpawnTasksParams<'a, B: BlockT, C, BE> {
332
    pub task_manager: &'a TaskManager,
333
    pub client: Arc<C>,
334
    pub substrate_backend: Arc<BE>,
335
    pub frontier_backend: Arc<fc_db::Backend<B, C>>,
336
    pub filter_pool: Option<FilterPool>,
337
    pub overrides: Arc<dyn StorageOverride<B>>,
338
    pub fee_history_limit: u64,
339
    pub fee_history_cache: FeeHistoryCache,
340
    /// Chain syncing service
341
    pub sync_service: Arc<SyncingService<B>>,
342
    /// Chain syncing service
343
    pub pubsub_notification_sinks: Arc<
344
        fc_mapping_sync::EthereumBlockNotificationSinks<
345
            fc_mapping_sync::EthereumBlockNotification<B>,
346
        >,
347
    >,
348
}
349

            
350
use fc_mapping_sync::{kv::MappingSyncWorker, SyncStrategy};
351
/// Spawn the tasks that are required to run Moonbeam.
352
138
pub fn spawn_essential_tasks<B, C, BE>(params: SpawnTasksParams<B, C, BE>)
353
138
where
354
138
    C: ProvideRuntimeApi<B> + BlockOf,
355
138
    C: HeaderBackend<B> + HeaderMetadata<B, Error = BlockChainError> + 'static,
356
138
    C: BlockchainEvents<B> + StorageProvider<B, BE>,
357
138
    C: Send + Sync + 'static,
358
138
    C::Api: EthereumRuntimeRPCApi<B>,
359
138
    C::Api: BlockBuilder<B>,
360
138
    B: BlockT<Hash = H256> + Send + Sync + 'static,
361
138
    B::Header: HeaderT<Number = u32>,
362
138
    BE: Backend<B> + 'static,
363
138
    BE::State: StateBackend<BlakeTwo256>,
364
138
{
365
138
    // Frontier offchain DB task. Essential.
366
138
    // Maps emulated ethereum data to substrate native data.
367
138
    match &*params.frontier_backend {
368
138
        fc_db::Backend::KeyValue(b) => {
369
138
            params.task_manager.spawn_essential_handle().spawn(
370
138
                "frontier-mapping-sync-worker",
371
138
                Some("frontier"),
372
138
                MappingSyncWorker::new(
373
138
                    params.client.import_notification_stream(),
374
138
                    Duration::new(6, 0),
375
138
                    params.client.clone(),
376
138
                    params.substrate_backend.clone(),
377
138
                    params.overrides.clone(),
378
138
                    b.clone(),
379
138
                    3,
380
138
                    0,
381
138
                    SyncStrategy::Parachain,
382
138
                    params.sync_service.clone(),
383
138
                    params.pubsub_notification_sinks.clone(),
384
138
                )
385
2156
                .for_each(|()| futures::future::ready(())),
386
138
            );
387
138
        }
388
        fc_db::Backend::Sql(b) => {
389
            params.task_manager.spawn_essential_handle().spawn_blocking(
390
                "frontier-mapping-sync-worker",
391
                Some("frontier"),
392
                fc_mapping_sync::sql::SyncWorker::run(
393
                    params.client.clone(),
394
                    params.substrate_backend.clone(),
395
                    b.clone(),
396
                    params.client.import_notification_stream(),
397
                    fc_mapping_sync::sql::SyncWorkerConfig {
398
                        read_notification_timeout: Duration::from_secs(10),
399
                        check_indexed_blocks_interval: Duration::from_secs(60),
400
                    },
401
                    fc_mapping_sync::SyncStrategy::Parachain,
402
                    params.sync_service.clone(),
403
                    params.pubsub_notification_sinks.clone(),
404
                ),
405
            );
406
        }
407
    }
408

            
409
    // Frontier `EthFilterApi` maintenance.
410
    // Manages the pool of user-created Filters.
411
138
    if let Some(filter_pool) = params.filter_pool {
412
138
        // Each filter is allowed to stay in the pool for 100 blocks.
413
138
        // TODO: Re-visit this assumption with parathreads, as they
414
138
        // might have a block every good amount of time, and can be abused
415
138
        // likely we will need to implement a time-based filter
416
138
        const FILTER_RETAIN_THRESHOLD: u64 = 100;
417
138
        params.task_manager.spawn_essential_handle().spawn(
418
138
            "frontier-filter-pool",
419
138
            Some("frontier"),
420
138
            EthTask::filter_pool_task(
421
138
                Arc::clone(&params.client),
422
138
                filter_pool,
423
138
                FILTER_RETAIN_THRESHOLD,
424
138
            ),
425
138
        );
426
138
    }
427

            
428
    // Spawn Frontier FeeHistory cache maintenance task.
429
138
    params.task_manager.spawn_essential_handle().spawn(
430
138
        "frontier-fee-history",
431
138
        Some("frontier"),
432
138
        EthTask::fee_history_task(
433
138
            Arc::clone(&params.client),
434
138
            Arc::clone(&params.overrides),
435
138
            params.fee_history_cache,
436
138
            params.fee_history_limit,
437
138
        ),
438
138
    );
439
138
}
440

            
441
/// A set of APIs that polkadot-like runtimes must implement.
442
///
443
/// This trait has no methods or associated type. It is a concise marker for all the trait bounds
444
/// that it contains.
445
pub trait RuntimeApiCollection:
446
    sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
447
    + sp_api::ApiExt<Block>
448
    + sp_block_builder::BlockBuilder<Block>
449
    + substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>
450
    + sp_api::Metadata<Block>
451
    + sp_offchain::OffchainWorkerApi<Block>
452
    + sp_session::SessionKeys<Block>
453
    + fp_rpc::ConvertTransactionRuntimeApi<Block>
454
    + fp_rpc::EthereumRuntimeRPCApi<Block>
455
    + cumulus_primitives_core::CollectCollationInfo<Block>
456
{
457
}
458

            
459
impl<Api> RuntimeApiCollection for Api where
460
    Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
461
        + sp_api::ApiExt<Block>
462
        + sp_block_builder::BlockBuilder<Block>
463
        + substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>
464
        + sp_api::Metadata<Block>
465
        + sp_offchain::OffchainWorkerApi<Block>
466
        + sp_session::SessionKeys<Block>
467
        + fp_rpc::ConvertTransactionRuntimeApi<Block>
468
        + fp_rpc::EthereumRuntimeRPCApi<Block>
469
        + cumulus_primitives_core::CollectCollationInfo<Block>
470
{
471
}
472

            
473
tp_traits::alias!(
474
    pub trait FrontierRpcRuntimeApi:
475
        MinimalContainerRuntimeApi +
476
        sp_api::ConstructRuntimeApi<
477
            Block,
478
            ContainerChainClient<Self>,
479
            RuntimeApi:
480
                RuntimeApiCollection
481
        >
482
);
483

            
484
#[derive(CloneNoBound)]
485
pub struct GenerateFrontierRpcBuilder<RuntimeApi> {
486
    pub rpc_config: crate::cli::RpcConfig,
487
    pub phantom: PhantomData<RuntimeApi>,
488
}
489

            
490
const _: () = {
491
    use tc_service_container_chain::rpc::generate_rpc_builder::*;
492

            
493
    impl<RuntimeApi: FrontierRpcRuntimeApi> GenerateRpcBuilder<RuntimeApi>
494
        for GenerateFrontierRpcBuilder<RuntimeApi>
495
    {
496
        fn generate(
497
            &self,
498
            GenerateRpcBuilderParams {
499
                backend,
500
                client,
501
                network,
502
                container_chain_config,
503
                prometheus_registry,
504
                sync_service,
505
                task_manager,
506
                transaction_pool,
507
                ..
508
            }: GenerateRpcBuilderParams<RuntimeApi>,
509
        ) -> Result<CompleteRpcBuilder, ServiceError> {
510
            let max_past_logs = self.rpc_config.max_past_logs;
511

            
512
            // Frontier specific stuff
513
            let filter_pool: Option<FilterPool> = Some(Arc::new(Mutex::new(BTreeMap::new())));
514
            let fee_history_cache: FeeHistoryCache = Arc::new(Mutex::new(BTreeMap::new()));
515
            let frontier_backend = Arc::new(fc_db::Backend::KeyValue(
516
                crate::service::open_frontier_backend(client.clone(), container_chain_config)?
517
                    .into(),
518
            ));
519
            let overrides = Arc::new(fc_rpc::StorageOverrideHandler::new(client.clone()));
520
            let fee_history_limit = self.rpc_config.fee_history_limit;
521

            
522
            let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks<
523
                fc_mapping_sync::EthereumBlockNotification<Block>,
524
            > = Default::default();
525
            let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks);
526

            
527
            spawn_essential_tasks(SpawnTasksParams {
528
                task_manager,
529
                client: client.clone(),
530
                substrate_backend: backend.clone(),
531
                frontier_backend: frontier_backend.clone(),
532
                filter_pool: filter_pool.clone(),
533
                overrides: overrides.clone(),
534
                fee_history_limit,
535
                fee_history_cache: fee_history_cache.clone(),
536
                sync_service: sync_service.clone(),
537
                pubsub_notification_sinks: pubsub_notification_sinks.clone(),
538
            });
539

            
540
            let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new(
541
                task_manager.spawn_handle(),
542
                overrides.clone(),
543
                self.rpc_config.eth_log_block_cache,
544
                self.rpc_config.eth_statuses_cache,
545
                prometheus_registry.clone(),
546
            ));
547

            
548
            Ok(Box::new(move |deny_unsafe, subscription_task_executor| {
549
                let deps = crate::rpc::FullDeps {
550
                    backend: backend.clone(),
551
                    client: client.clone(),
552
                    deny_unsafe,
553
                    filter_pool: filter_pool.clone(),
554
                    frontier_backend: match &*frontier_backend {
555
                        fc_db::Backend::KeyValue(b) => b.clone(),
556
                        fc_db::Backend::Sql(b) => b.clone(),
557
                    },
558
                    graph: transaction_pool.pool().clone(),
559
                    pool: transaction_pool.clone(),
560
                    max_past_logs,
561
                    fee_history_limit,
562
                    fee_history_cache: fee_history_cache.clone(),
563
                    network: Arc::new(network.clone()),
564
                    sync: sync_service.clone(),
565
                    block_data_cache: block_data_cache.clone(),
566
                    overrides: overrides.clone(),
567
                    is_authority: false,
568
                    command_sink: None,
569
                    xcm_senders: None,
570
                };
571
                crate::rpc::create_full(
572
                    deps,
573
                    subscription_task_executor,
574
                    pubsub_notification_sinks.clone(),
575
                )
576
                .map_err(Into::into)
577
            }))
578
        }
579
    }
580
};