1use crate::network::build_network;
2use crate::providers::{BlockImportProvider, RpcProvider};
3use crate::{FullBackend, FullClient};
4use cross_domain_message_gossip::ChainMsg;
5use domain_block_builder::CustomGenesisBlockBuilder;
6use domain_block_preprocessor::inherents::CreateInherentDataProvider;
7use domain_client_message_relayer::GossipMessageSink;
8use domain_client_operator::snap_sync::ConsensusChainSyncParams;
9use domain_client_operator::{Operator, OperatorParams, OperatorStreams};
10use domain_runtime_primitives::opaque::{Block, Header};
11use domain_runtime_primitives::{Balance, Hash};
12use futures::Stream;
13use futures::channel::mpsc;
14use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi;
15use sc_client_api::{
16 AuxStore, BlockBackend, BlockImportNotification, BlockchainEvents, ExecutorProvider,
17 ProofProvider,
18};
19use sc_consensus::{BasicQueue, BoxBlockImport};
20use sc_domains::{ExtensionsFactory, RuntimeExecutor};
21use sc_network::service::traits::NetworkService;
22use sc_network::{NetworkPeers, NetworkWorker, NotificationMetrics};
23use sc_service::{
24 BuildNetworkParams, Configuration as ServiceConfiguration, PartialComponents, SpawnTasksParams,
25 TFullBackend, TaskManager,
26};
27use sc_telemetry::{Telemetry, TelemetryWorker, TelemetryWorkerHandle};
28use sc_transaction_pool::{BasicPool, FullChainApi};
29use sc_transaction_pool_api::OffchainTransactionPoolFactory;
30use sc_utils::mpsc::{TracingUnboundedReceiver, tracing_unbounded};
31use serde::de::DeserializeOwned;
32use sp_api::{ApiExt, ConstructRuntimeApi, Metadata, ProvideRuntimeApi};
33use sp_block_builder::BlockBuilder;
34use sp_blockchain::{HeaderBackend, HeaderMetadata};
35use sp_consensus::SyncOracle;
36use sp_consensus_slots::Slot;
37use sp_core::traits::SpawnEssentialNamed;
38use sp_core::{Decode, Encode, H256};
39use sp_domains::core_api::DomainCoreApi;
40use sp_domains::{BundleProducerElectionApi, DomainId, DomainsApi, OperatorId};
41use sp_domains_fraud_proof::FraudProofApi;
42use sp_messenger::messages::ChainId;
43use sp_messenger::{MessengerApi, RelayerApi};
44use sp_mmr_primitives::MmrApi;
45use sp_offchain::OffchainWorkerApi;
46use sp_runtime::traits::{Block as BlockT, NumberFor};
47use sp_session::SessionKeys;
48use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
49use std::fmt::{Debug, Display};
50use std::marker::PhantomData;
51use std::str::FromStr;
52use std::sync::Arc;
53use subspace_core_primitives::pot::PotOutput;
54use subspace_runtime_primitives::{HeaderFor, Nonce};
55use substrate_frame_rpc_system::AccountNonceApi;
56
57pub type DomainOperator<Block, CBlock, CClient, RuntimeApi> = Operator<
58 Block,
59 CBlock,
60 FullClient<Block, RuntimeApi>,
61 CClient,
62 FullPool<RuntimeApi>,
63 FullBackend<Block>,
64 RuntimeExecutor,
65>;
66
67pub struct NewFull<C, CodeExecutor, CBlock, CClient, RuntimeApi, AccountId>
69where
70 Block: BlockT,
71 CBlock: BlockT,
72 NumberFor<CBlock>: From<NumberFor<Block>>,
73 CBlock::Hash: From<Hash>,
74 CClient: HeaderBackend<CBlock>
75 + BlockBackend<CBlock>
76 + ProvideRuntimeApi<CBlock>
77 + Send
78 + Sync
79 + 'static,
80 CClient::Api:
81 DomainsApi<CBlock, Header> + MessengerApi<CBlock, NumberFor<CBlock>, CBlock::Hash>,
82 RuntimeApi: ConstructRuntimeApi<Block, FullClient<Block, RuntimeApi>> + Send + Sync + 'static,
83 RuntimeApi::RuntimeApi: ApiExt<Block>
84 + Metadata<Block>
85 + AccountNonceApi<Block, AccountId, Nonce>
86 + BlockBuilder<Block>
87 + OffchainWorkerApi<Block>
88 + SessionKeys<Block>
89 + TaggedTransactionQueue<Block>
90 + TransactionPaymentRuntimeApi<Block, Balance>
91 + DomainCoreApi<Block>
92 + MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>
93 + RelayerApi<Block, NumberFor<Block>, NumberFor<CBlock>, CBlock::Hash>,
94 AccountId: Encode + Decode,
95{
96 pub task_manager: TaskManager,
98 pub client: C,
100 pub backend: Arc<FullBackend<Block>>,
102 pub code_executor: Arc<CodeExecutor>,
104 pub network_service: Arc<dyn NetworkService>,
106 pub sync_service: Arc<sc_network_sync::SyncingService<Block>>,
108 pub rpc_handlers: sc_service::RpcHandlers,
110 pub operator: DomainOperator<Block, CBlock, CClient, RuntimeApi>,
112 pub transaction_pool: Arc<FullPool<RuntimeApi>>,
114
115 _phantom_data: PhantomData<AccountId>,
116}
117
118pub type FullPool<RuntimeApi> =
120 BasicPool<FullChainApi<FullClient<Block, RuntimeApi>, Block>, Block>;
121
122#[allow(clippy::type_complexity)]
124#[expect(clippy::result_large_err, reason = "Comes from Substrate")]
125fn new_partial<RuntimeApi, CBlock, CClient, BIMP>(
126 domain_id: DomainId,
127 config: &ServiceConfiguration,
128 consensus_client: Arc<CClient>,
129 domain_backend: Arc<FullBackend<Block>>,
130 block_import_provider: &BIMP,
131 confirmation_depth_k: NumberFor<CBlock>,
132 snap_sync: bool,
133) -> Result<
134 PartialComponents<
135 FullClient<Block, RuntimeApi>,
136 FullBackend<Block>,
137 (),
138 sc_consensus::DefaultImportQueue<Block>,
139 FullPool<RuntimeApi>,
140 (
141 Option<Telemetry>,
142 Option<TelemetryWorkerHandle>,
143 Arc<RuntimeExecutor>,
144 BoxBlockImport<Block>,
145 ),
146 >,
147 sc_service::Error,
148>
149where
150 CBlock: BlockT,
151 NumberFor<CBlock>: From<NumberFor<Block>> + Into<u32>,
152 CBlock::Hash: From<Hash> + Into<Hash>,
153 CClient: HeaderBackend<CBlock>
154 + BlockBackend<CBlock>
155 + ProvideRuntimeApi<CBlock>
156 + Send
157 + Sync
158 + 'static,
159 CClient::Api: DomainsApi<CBlock, Header>
160 + MessengerApi<CBlock, NumberFor<CBlock>, CBlock::Hash>
161 + MmrApi<CBlock, H256, NumberFor<CBlock>>,
162 RuntimeApi: ConstructRuntimeApi<Block, FullClient<Block, RuntimeApi>> + Send + Sync + 'static,
163 RuntimeApi::RuntimeApi: TaggedTransactionQueue<Block>
164 + MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>
165 + ApiExt<Block>,
166 BIMP: BlockImportProvider<Block, FullClient<Block, RuntimeApi>>,
167{
168 let telemetry = config
169 .telemetry_endpoints
170 .clone()
171 .filter(|x| !x.is_empty())
172 .map(|endpoints| -> Result<_, sc_telemetry::Error> {
173 let worker = TelemetryWorker::new(16)?;
174 let telemetry = worker.handle().new_telemetry(endpoints);
175 Ok((worker, telemetry))
176 })
177 .transpose()?;
178
179 let executor = sc_service::new_wasm_executor(&config.executor);
180
181 let genesis_block_builder = CustomGenesisBlockBuilder::<_, CBlock, _, _, _>::new(
182 domain_id,
183 consensus_client.clone(),
184 config.chain_spec.as_storage_builder(),
185 !snap_sync,
186 domain_backend.clone(),
187 executor.clone(),
188 )?;
189
190 let (client, backend, keystore_container, task_manager) =
191 sc_service::new_full_parts_with_genesis_builder(
192 config,
193 telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
194 executor.clone(),
195 domain_backend,
196 genesis_block_builder,
197 false,
198 )?;
199
200 let client = Arc::new(client);
201
202 let executor = Arc::new(executor);
203 client.execution_extensions().set_extensions_factory(
204 ExtensionsFactory::<_, CBlock, Block, _>::new(
205 consensus_client.clone(),
206 executor.clone(),
207 confirmation_depth_k.into(),
208 ),
209 );
210
211 let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle());
212
213 let telemetry = telemetry.map(|(worker, telemetry)| {
214 task_manager
215 .spawn_handle()
216 .spawn("telemetry", None, worker.run());
217 telemetry
218 });
219
220 let transaction_pool = Arc::from(BasicPool::new_full(
221 Default::default(),
222 config.role.is_authority().into(),
223 config.prometheus_registry(),
224 task_manager.spawn_essential_handle(),
225 client.clone(),
226 ));
227
228 let import_queue = BasicQueue::new(
229 domain_client_consensus_relay_chain::Verifier::default(),
230 Box::new(block_import_provider.block_import(client.clone())),
231 None,
232 &task_manager.spawn_essential_handle(),
233 config.prometheus_registry(),
234 );
235
236 let params = PartialComponents {
237 backend,
238 client: client.clone(),
239 import_queue,
240 keystore_container,
241 task_manager,
242 transaction_pool,
243 select_chain: (),
244 other: (
245 telemetry,
246 telemetry_worker_handle,
247 executor,
248 Box::new(block_import_provider.block_import(client)) as Box<_>,
249 ),
250 };
251
252 Ok(params)
253}
254
255pub struct DomainParams<CBlock, CClient, IBNS, CIBNS, NSNS, ASS, Provider>
256where
257 CBlock: BlockT,
258{
259 pub domain_id: DomainId,
260 pub domain_config: ServiceConfiguration,
261 pub domain_created_at: NumberFor<CBlock>,
262 pub maybe_operator_id: Option<OperatorId>,
263 pub consensus_client: Arc<CClient>,
264 pub consensus_network: Arc<dyn NetworkPeers + Send + Sync>,
265 pub consensus_offchain_tx_pool_factory: OffchainTransactionPoolFactory<CBlock>,
266 pub domain_sync_oracle: Arc<dyn SyncOracle + Send + Sync>,
267 pub operator_streams: OperatorStreams<CBlock, IBNS, CIBNS, NSNS, ASS>,
268 pub gossip_message_sink: GossipMessageSink,
269 pub domain_message_receiver: TracingUnboundedReceiver<ChainMsg>,
270 pub provider: Provider,
271 pub skip_empty_bundle_production: bool,
272 pub skip_out_of_order_slot: bool,
273 pub confirmation_depth_k: NumberFor<CBlock>,
274 pub challenge_period: NumberFor<CBlock>,
275 pub consensus_chain_sync_params: Option<ConsensusChainSyncParams<CBlock, HeaderFor<Block>>>,
276 pub domain_backend: Arc<FullBackend<Block>>,
277}
278
279#[expect(clippy::result_large_err, reason = "Comes from Substrate")]
281pub async fn new_full<CBlock, CClient, IBNS, CIBNS, NSNS, ASS, RuntimeApi, AccountId, Provider>(
282 domain_params: DomainParams<CBlock, CClient, IBNS, CIBNS, NSNS, ASS, Provider>,
283) -> sc_service::error::Result<
284 NewFull<
285 Arc<FullClient<Block, RuntimeApi>>,
286 RuntimeExecutor,
287 CBlock,
288 CClient,
289 RuntimeApi,
290 AccountId,
291 >,
292>
293where
294 CBlock: BlockT,
295 NumberFor<CBlock>: From<NumberFor<Block>> + Into<u32>,
296 CBlock::Hash: From<Hash> + Into<Hash>,
297 CClient: HeaderBackend<CBlock>
298 + HeaderMetadata<CBlock, Error = sp_blockchain::Error>
299 + BlockBackend<CBlock>
300 + ProofProvider<CBlock>
301 + ProvideRuntimeApi<CBlock>
302 + BlockchainEvents<CBlock>
303 + AuxStore
304 + Send
305 + Sync
306 + 'static,
307 CClient::Api: DomainsApi<CBlock, Header>
308 + RelayerApi<CBlock, NumberFor<CBlock>, NumberFor<CBlock>, CBlock::Hash>
309 + MessengerApi<CBlock, NumberFor<CBlock>, CBlock::Hash>
310 + BundleProducerElectionApi<CBlock, subspace_runtime_primitives::Balance>
311 + FraudProofApi<CBlock, Header>
312 + MmrApi<CBlock, H256, NumberFor<CBlock>>,
313 IBNS: Stream<Item = (NumberFor<CBlock>, mpsc::Sender<()>)> + Send + Unpin + 'static,
314 CIBNS: Stream<Item = BlockImportNotification<CBlock>> + Send + Unpin + 'static,
315 NSNS: Stream<Item = (Slot, PotOutput)> + Send + 'static,
316 ASS: Stream<Item = mpsc::Sender<()>> + Send + 'static,
317 RuntimeApi: ConstructRuntimeApi<Block, FullClient<Block, RuntimeApi>> + Send + Sync + 'static,
318 RuntimeApi::RuntimeApi: ApiExt<Block>
319 + Metadata<Block>
320 + BlockBuilder<Block>
321 + OffchainWorkerApi<Block>
322 + SessionKeys<Block>
323 + DomainCoreApi<Block>
324 + MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>
325 + TaggedTransactionQueue<Block>
326 + AccountNonceApi<Block, AccountId, Nonce>
327 + TransactionPaymentRuntimeApi<Block, Balance>
328 + RelayerApi<Block, NumberFor<Block>, NumberFor<CBlock>, CBlock::Hash>,
329 AccountId: DeserializeOwned
330 + Encode
331 + Decode
332 + Clone
333 + Debug
334 + Display
335 + FromStr
336 + Sync
337 + Send
338 + 'static,
339 Provider: RpcProvider<
340 Block,
341 FullClient<Block, RuntimeApi>,
342 FullPool<RuntimeApi>,
343 TFullBackend<Block>,
344 AccountId,
345 CreateInherentDataProvider<CClient, CBlock>,
346 > + BlockImportProvider<Block, FullClient<Block, RuntimeApi>>
347 + 'static,
348{
349 let DomainParams {
350 domain_id,
351 maybe_operator_id,
352 mut domain_config,
353 domain_created_at,
354 consensus_client,
355 consensus_offchain_tx_pool_factory,
356 domain_sync_oracle,
357 consensus_network,
358 operator_streams,
359 gossip_message_sink,
360 domain_message_receiver,
361 provider,
362 skip_empty_bundle_production,
363 skip_out_of_order_slot,
364 confirmation_depth_k,
365 consensus_chain_sync_params,
366 challenge_period,
367 domain_backend,
368 } = domain_params;
369
370 let params = new_partial(
374 domain_id,
375 &domain_config,
376 consensus_client.clone(),
377 domain_backend,
378 &provider,
379 confirmation_depth_k,
380 consensus_chain_sync_params.is_some(),
381 )?;
382
383 let (mut telemetry, _telemetry_worker_handle, code_executor, block_import) = params.other;
384
385 let client = params.client.clone();
386 let backend = params.backend.clone();
387
388 let transaction_pool = params.transaction_pool.clone();
389 let mut task_manager = params.task_manager;
390 let net_config = sc_network::config::FullNetworkConfiguration::<_, _, NetworkWorker<_, _>>::new(
391 &domain_config.network,
392 domain_config
393 .prometheus_config
394 .as_ref()
395 .map(|cfg| cfg.registry.clone()),
396 );
397
398 let (
399 network_service,
400 system_rpc_tx,
401 tx_handler_controller,
402 sync_service,
403 network_service_handle,
404 block_downloader,
405 ) = build_network(BuildNetworkParams {
406 config: &domain_config,
407 net_config,
408 client: client.clone(),
409 transaction_pool: transaction_pool.clone(),
410 spawn_handle: task_manager.spawn_handle(),
411 import_queue: params.import_queue,
412 block_announce_validator_builder: None,
414 warp_sync_config: None,
415 block_relay: None,
416 metrics: NotificationMetrics::new(
417 domain_config
418 .prometheus_config
419 .as_ref()
420 .map(|cfg| &cfg.registry),
421 ),
422 })?;
423
424 let fork_id = domain_config.chain_spec.fork_id().map(String::from);
425
426 let is_authority = domain_config.role.is_authority();
427 domain_config.rpc.id_provider = provider.rpc_id();
428 let rpc_builder = {
429 let deps = crate::rpc::FullDeps {
430 client: client.clone(),
431 pool: transaction_pool.clone(),
432 network: network_service.clone(),
433 sync: sync_service.clone(),
434 is_authority,
435 prometheus_registry: domain_config.prometheus_registry().cloned(),
436 database_source: domain_config.database.clone(),
437 task_spawner: task_manager.spawn_handle(),
438 backend: backend.clone(),
439 create_inherent_data_provider: CreateInherentDataProvider::new(
444 consensus_client.clone(),
445 None,
447 domain_id,
448 ),
449 };
450
451 let spawn_essential = task_manager.spawn_essential_handle();
452 let rpc_deps = provider.deps(deps)?;
453 #[allow(clippy::result_large_err)]
454 Box::new(move |subscription_task_executor| {
455 let spawn_essential = spawn_essential.clone();
456 provider
457 .rpc_builder(
458 rpc_deps.clone(),
459 subscription_task_executor,
460 spawn_essential,
461 )
462 .map_err(Into::into)
463 })
464 };
465
466 let rpc_handlers = sc_service::spawn_tasks(SpawnTasksParams {
467 rpc_builder,
468 client: client.clone(),
469 transaction_pool: transaction_pool.clone(),
470 task_manager: &mut task_manager,
471 config: domain_config,
472 keystore: params.keystore_container.keystore(),
473 backend: backend.clone(),
474 network: network_service.clone(),
475 system_rpc_tx,
476 tx_handler_controller,
477 sync_service: sync_service.clone(),
478 telemetry: telemetry.as_mut(),
479 tracing_execute_block: None,
481 })?;
482
483 let spawn_essential = task_manager.spawn_essential_handle();
484 let (bundle_sender, _bundle_receiver) = tracing_unbounded("domain_bundle_stream", 100);
485
486 let operator = Operator::new(
487 Box::new(spawn_essential.clone()),
488 OperatorParams {
489 domain_id,
490 domain_created_at,
491 consensus_client: consensus_client.clone(),
492 consensus_offchain_tx_pool_factory,
493 domain_sync_oracle: domain_sync_oracle.clone(),
494 client: client.clone(),
495 transaction_pool: transaction_pool.clone(),
496 backend: backend.clone(),
497 code_executor: code_executor.clone(),
498 maybe_operator_id,
499 keystore: params.keystore_container.keystore(),
500 bundle_sender: Arc::new(bundle_sender),
501 operator_streams,
502 consensus_confirmation_depth_k: confirmation_depth_k,
503 block_import: Arc::new(block_import),
504 skip_empty_bundle_production,
505 skip_out_of_order_slot,
506 sync_service: sync_service.clone(),
507 network_service: Arc::clone(&network_service),
508 block_downloader,
509 consensus_chain_sync_params,
510 domain_fork_id: fork_id,
511 domain_network_service_handle: network_service_handle,
512 challenge_period,
513 },
514 )
515 .await?;
516
517 if is_authority {
518 let relayer_worker = domain_client_message_relayer::worker::start_relaying_messages(
519 domain_id,
520 consensus_client.clone(),
521 client.clone(),
522 confirmation_depth_k,
523 domain_sync_oracle.clone(),
526 gossip_message_sink.clone(),
527 );
528
529 spawn_essential.spawn_essential_blocking("domain-relayer", None, Box::pin(relayer_worker));
530
531 let channel_update_worker =
532 domain_client_message_relayer::worker::gossip_channel_updates::<_, _, CBlock, _>(
533 ChainId::Domain(domain_id),
534 client.clone(),
535 domain_sync_oracle.clone(),
538 gossip_message_sink,
539 );
540
541 spawn_essential.spawn_essential_blocking(
542 "domain-channel-update-worker",
543 None,
544 Box::pin(channel_update_worker),
545 );
546 }
547
548 let domain_listener =
550 cross_domain_message_gossip::start_cross_chain_message_listener::<_, _, _, _, _, _, _>(
551 ChainId::Domain(domain_id),
552 consensus_client.clone(),
553 client.clone(),
554 params.transaction_pool.clone(),
555 consensus_network,
556 domain_message_receiver,
557 code_executor.clone(),
558 domain_sync_oracle,
559 );
560
561 spawn_essential.spawn_essential_blocking(
562 "domain-message-listener",
563 None,
564 Box::pin(domain_listener),
565 );
566
567 let new_full = NewFull {
568 task_manager,
569 client,
570 backend,
571 code_executor,
572 network_service,
573 sync_service,
574 rpc_handlers,
575 operator,
576 transaction_pool: params.transaction_pool,
577 _phantom_data: Default::default(),
578 };
579
580 Ok(new_full)
581}