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::SubscriptionTaskExecutor;
25

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

            
70
type ParachainExecutor = WasmExecutor<ParachainHostFunctions>;
71
type ParachainClient = TFullClient<Block, RuntimeApi, ParachainExecutor>;
72

            
73
type FullPool<Client> =
74
    sc_transaction_pool::BasicPool<sc_transaction_pool::FullChainApi<Client, Block>, Block>;
75

            
76
pub struct DefaultEthConfig<C, BE>(std::marker::PhantomData<(C, BE)>);
77

            
78
impl<C, BE> fc_rpc::EthConfig<Block, C> for DefaultEthConfig<C, BE>
79
where
80
    C: StorageProvider<Block, BE> + Sync + Send + 'static,
81
    BE: Backend<Block> + 'static,
82
{
83
    type EstimateGasAdapter = ();
84
    type RuntimeStorageOverride =
85
        fc_rpc::frontier_backend_client::SystemAccountId20StorageOverride<Block, C, BE>;
86
}
87

            
88
mod eth;
89
pub use eth::*;
90
mod finality;
91

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

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

            
162
292
    let mut io = RpcModule::new(());
163
292
    let FullDeps {
164
292
        client,
165
292
        pool,
166
292
        graph,
167
292
        network,
168
292
        sync,
169
292
        filter_pool,
170
292
        frontier_backend,
171
292
        backend: _,
172
292
        max_past_logs,
173
292
        fee_history_limit,
174
292
        fee_history_cache,
175
292
        overrides,
176
292
        block_data_cache,
177
292
        is_authority,
178
292
        command_sink,
179
292
        xcm_senders,
180
292
    } = deps;
181
292

            
182
292
    io.merge(System::new(Arc::clone(&client), Arc::clone(&pool)).into_rpc())?;
183

            
184
    // TODO: are we supporting signing?
185
292
    let signers = Vec::new();
186

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

            
200
292
    let pending_create_inherent_data_providers = move |_, _| {
201
4
        let authorities_for_cidp = authorities.clone();
202

            
203
4
        async move {
204
4
            let mocked_authorities_noting =
205
4
                ccp_authorities_noting_inherent::MockAuthoritiesNotingInherentDataProvider {
206
4
                    current_para_block: 1000,
207
4
                    relay_offset: 1000,
208
4
                    relay_blocks_per_para_block: 2,
209
4
                    orchestrator_para_id: 1000u32.into(),
210
4
                    container_para_id: 2000u32.into(),
211
4
                    authorities: authorities_for_cidp,
212
4
                };
213
4

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

            
244
292
    let pending_consensus_data_provider_frontier: Option<
245
292
        Box<(dyn fc_rpc::pending::ConsensusDataProvider<_>)>,
246
292
    > = Some(Box::new(
247
292
        tc_consensus::ContainerManualSealAuraConsensusDataProvider::new(
248
292
            SlotDuration::from_millis(container_chain_template_frontier_runtime::SLOT_DURATION),
249
292
            authorities_for_cdp,
250
292
        ),
251
292
    ));
252
292

            
253
292
    io.merge(
254
292
        Eth::<_, _, _, _, _, _, _, DefaultEthConfig<C, BE>>::new(
255
292
            Arc::clone(&client),
256
292
            Arc::clone(&pool),
257
292
            Arc::clone(&graph),
258
292
            convert_transaction,
259
292
            Arc::clone(&sync),
260
292
            signers,
261
292
            Arc::clone(&overrides),
262
292
            Arc::clone(&frontier_backend),
263
292
            is_authority,
264
292
            Arc::clone(&block_data_cache),
265
292
            fee_history_cache,
266
292
            fee_history_limit,
267
292
            10,
268
292
            None,
269
292
            pending_create_inherent_data_providers,
270
292
            pending_consensus_data_provider_frontier,
271
292
        )
272
292
        .into_rpc(),
273
292
    )?;
274

            
275
292
    let tx_pool = TxPool::new(client.clone(), graph.clone());
276
292
    if let Some(filter_pool) = filter_pool {
277
292
        io.merge(
278
292
            EthFilter::new(
279
292
                client.clone(),
280
292
                frontier_backend.clone(),
281
292
                graph,
282
292
                filter_pool,
283
292
                500_usize, // max stored filters
284
292
                max_past_logs,
285
292
                block_data_cache,
286
292
            )
287
292
            .into_rpc(),
288
292
        )?;
289
    }
290

            
291
292
    io.merge(
292
292
        Net::new(
293
292
            Arc::clone(&client),
294
292
            network,
295
292
            // Whether to format the `peer_count` response as Hex (default) or not.
296
292
            true,
297
292
        )
298
292
        .into_rpc(),
299
292
    )?;
300

            
301
292
    if let Some(command_sink) = command_sink {
302
292
        io.merge(
303
292
            // We provide the rpc handler with the sending end of the channel to allow the rpc
304
292
            // send EngineCommands to the background block authorship task.
305
292
            ManualSeal::new(command_sink).into_rpc(),
306
292
        )?;
307
    };
308

            
309
292
    io.merge(Web3::new(Arc::clone(&client)).into_rpc())?;
310
292
    io.merge(
311
292
        EthPubSub::new(
312
292
            pool,
313
292
            Arc::clone(&client),
314
292
            sync,
315
292
            subscription_task_executor,
316
292
            overrides,
317
292
            pubsub_notification_sinks,
318
292
        )
319
292
        .into_rpc(),
320
292
    )?;
321
292
    io.merge(tx_pool.into_rpc())?;
322

            
323
292
    if let Some((downward_message_channel, hrmp_message_channel)) = xcm_senders {
324
292
        io.merge(
325
292
            ManualXcm {
326
292
                downward_message_channel,
327
292
                hrmp_message_channel,
328
292
            }
329
292
            .into_rpc(),
330
292
        )?;
331
    }
332

            
333
292
    io.merge(FrontierFinality::new(client.clone(), frontier_backend.clone()).into_rpc())?;
334

            
335
292
    Ok(io)
336
292
}
337

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

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

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

            
435
    // Spawn Frontier FeeHistory cache maintenance task.
436
146
    params.task_manager.spawn_essential_handle().spawn(
437
146
        "frontier-fee-history",
438
146
        Some("frontier"),
439
146
        EthTask::fee_history_task(
440
146
            Arc::clone(&params.client),
441
146
            Arc::clone(&params.overrides),
442
146
            params.fee_history_cache,
443
146
            params.fee_history_limit,
444
146
        ),
445
146
    );
446
146
}
447

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

            
466
impl<Api> RuntimeApiCollection for Api where
467
    Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
468
        + sp_api::ApiExt<Block>
469
        + sp_block_builder::BlockBuilder<Block>
470
        + substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>
471
        + sp_api::Metadata<Block>
472
        + sp_offchain::OffchainWorkerApi<Block>
473
        + sp_session::SessionKeys<Block>
474
        + fp_rpc::ConvertTransactionRuntimeApi<Block>
475
        + fp_rpc::EthereumRuntimeRPCApi<Block>
476
        + cumulus_primitives_core::CollectCollationInfo<Block>
477
{
478
}
479

            
480
tp_traits::alias!(
481
    pub trait FrontierRpcRuntimeApi:
482
        MinimalContainerRuntimeApi +
483
        sp_api::ConstructRuntimeApi<
484
            Block,
485
            ContainerChainClient<Self>,
486
            RuntimeApi:
487
                RuntimeApiCollection
488
        >
489
);
490

            
491
#[derive(CloneNoBound)]
492
pub struct GenerateFrontierRpcBuilder<RuntimeApi> {
493
    pub rpc_config: crate::cli::RpcConfig,
494
    pub phantom: PhantomData<RuntimeApi>,
495
}
496

            
497
const _: () = {
498
    use tc_service_container_chain::rpc::generate_rpc_builder::*;
499

            
500
    impl<RuntimeApi: FrontierRpcRuntimeApi> GenerateRpcBuilder<RuntimeApi>
501
        for GenerateFrontierRpcBuilder<RuntimeApi>
502
    {
503
        fn generate(
504
            &self,
505
            GenerateRpcBuilderParams {
506
                backend,
507
                client,
508
                network,
509
                container_chain_config,
510
                prometheus_registry,
511
                sync_service,
512
                task_manager,
513
                transaction_pool,
514
                ..
515
            }: GenerateRpcBuilderParams<RuntimeApi>,
516
        ) -> Result<CompleteRpcBuilder, ServiceError> {
517
            let max_past_logs = self.rpc_config.max_past_logs;
518

            
519
            // Frontier specific stuff
520
            let filter_pool: Option<FilterPool> = Some(Arc::new(Mutex::new(BTreeMap::new())));
521
            let fee_history_cache: FeeHistoryCache = Arc::new(Mutex::new(BTreeMap::new()));
522
            let frontier_backend = Arc::new(fc_db::Backend::KeyValue(
523
                crate::service::open_frontier_backend(client.clone(), container_chain_config)?
524
                    .into(),
525
            ));
526
            let overrides = Arc::new(fc_rpc::StorageOverrideHandler::new(client.clone()));
527
            let fee_history_limit = self.rpc_config.fee_history_limit;
528

            
529
            let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks<
530
                fc_mapping_sync::EthereumBlockNotification<Block>,
531
            > = Default::default();
532
            let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks);
533

            
534
            spawn_essential_tasks(SpawnTasksParams {
535
                task_manager,
536
                client: client.clone(),
537
                substrate_backend: backend.clone(),
538
                frontier_backend: frontier_backend.clone(),
539
                filter_pool: filter_pool.clone(),
540
                overrides: overrides.clone(),
541
                fee_history_limit,
542
                fee_history_cache: fee_history_cache.clone(),
543
                sync_service: sync_service.clone(),
544
                pubsub_notification_sinks: pubsub_notification_sinks.clone(),
545
            });
546

            
547
            let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new(
548
                task_manager.spawn_handle(),
549
                overrides.clone(),
550
                self.rpc_config.eth_log_block_cache,
551
                self.rpc_config.eth_statuses_cache,
552
                prometheus_registry.clone(),
553
            ));
554

            
555
            Ok(Box::new(move |subscription_task_executor| {
556
                let graph_pool = transaction_pool.0
557
                        .as_any()
558
                        .downcast_ref::<FullPool<ParachainClient>>()
559
                        .expect("Frontier container chain template supports only single state transaction pool! Use --pool-type=single-state");
560
                let deps = crate::rpc::FullDeps {
561
                    backend: backend.clone(),
562
                    client: client.clone(),
563
                    filter_pool: filter_pool.clone(),
564
                    frontier_backend: match &*frontier_backend {
565
                        fc_db::Backend::KeyValue(b) => b.clone(),
566
                        fc_db::Backend::Sql(b) => b.clone(),
567
                    },
568
                    graph: graph_pool.pool().clone(),
569
                    pool: transaction_pool.clone(),
570
                    max_past_logs,
571
                    fee_history_limit,
572
                    fee_history_cache: fee_history_cache.clone(),
573
                    network: Arc::new(network.clone()),
574
                    sync: sync_service.clone(),
575
                    block_data_cache: block_data_cache.clone(),
576
                    overrides: overrides.clone(),
577
                    is_authority: false,
578
                    command_sink: None,
579
                    xcm_senders: None,
580
                };
581
                crate::rpc::create_full(
582
                    deps,
583
                    subscription_task_executor,
584
                    pubsub_notification_sinks.clone(),
585
                )
586
                .map_err(Into::into)
587
            }))
588
        }
589
    }
590
};