1pub mod config;
4pub mod dsn;
5mod metrics;
6pub(crate) mod mmr;
7pub mod rpc;
8pub mod sync_from_dsn;
9mod task_spawner;
10mod utils;
11
12use crate::config::{ChainSyncMode, SubspaceConfiguration, SubspaceNetworking};
13use crate::dsn::{DsnConfigurationError, create_dsn_instance};
14use crate::metrics::NodeMetrics;
15use crate::mmr::request_handler::MmrRequestHandler;
16use crate::sync_from_dsn::DsnPieceGetter;
17use crate::sync_from_dsn::piece_validator::SegmentCommitmentPieceValidator;
18use crate::sync_from_dsn::snap_sync::snap_sync;
19use async_lock::Semaphore;
20use core::sync::atomic::{AtomicU32, Ordering};
21use cross_domain_message_gossip::xdm_gossip_peers_set_config;
22use domain_runtime_primitives::opaque::{Block as DomainBlock, Header as DomainHeader};
23use frame_system_rpc_runtime_api::AccountNonceApi;
24use futures::FutureExt;
25use futures::channel::oneshot;
26use jsonrpsee::RpcModule;
27use pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi;
28use parity_scale_codec::Decode;
29use parking_lot::Mutex;
30use prometheus_client::registry::Registry;
31use sc_basic_authorship::ProposerFactory;
32use sc_chain_spec::{ChainSpec, GenesisBlockBuilder};
33use sc_client_api::execution_extensions::ExtensionsFactory;
34use sc_client_api::{
35 AuxStore, Backend, BlockBackend, BlockchainEvents, ExecutorProvider, HeaderBackend,
36};
37use sc_consensus::{
38 BasicQueue, BlockCheckParams, BlockImport, BlockImportParams, BoxBlockImport,
39 DefaultImportQueue, ImportQueue, ImportResult,
40};
41use sc_consensus_slots::SlotProportion;
42use sc_consensus_subspace::SubspaceLink;
43use sc_consensus_subspace::archiver::{
44 ArchivedSegmentNotification, ObjectMappingNotification, SegmentHeadersStore,
45 create_subspace_archiver,
46};
47use sc_consensus_subspace::block_import::{BlockImportingNotification, SubspaceBlockImport};
48use sc_consensus_subspace::notification::SubspaceNotificationStream;
49use sc_consensus_subspace::slot_worker::{
50 NewSlotNotification, RewardSigningNotification, SubspaceSlotWorker, SubspaceSlotWorkerOptions,
51 SubspaceSyncOracle,
52};
53use sc_consensus_subspace::verifier::{SubspaceVerifier, SubspaceVerifierOptions};
54use sc_domains::ExtensionsFactory as DomainsExtensionFactory;
55use sc_domains::domain_block_er::execution_receipt_protocol::DomainBlockERRequestHandler;
56use sc_network::service::traits::NetworkService;
57use sc_network::{NetworkWorker, NotificationMetrics, NotificationService, Roles};
58use sc_network_sync::block_relay_protocol::BlockRelayParams;
59use sc_network_sync::engine::SyncingEngine;
60use sc_network_sync::service::network::NetworkServiceProvider;
61use sc_proof_of_time::source::gossip::pot_gossip_peers_set_config;
62use sc_proof_of_time::source::{PotSlotInfo, PotSourceWorker};
63use sc_proof_of_time::verifier::PotVerifier;
64use sc_service::error::Error as ServiceError;
65use sc_service::{
66 BuildNetworkAdvancedParams, Configuration, SpawnTasksParams, TaskManager,
67 build_network_advanced, build_polkadot_syncing_strategy,
68};
69use sc_subspace_block_relay::{
70 BlockRelayConfigurationError, NetworkWrapper, build_consensus_relay,
71};
72use sc_telemetry::{Telemetry, TelemetryWorker};
73use sc_transaction_pool::TransactionPoolHandle;
74use sc_transaction_pool_api::OffchainTransactionPoolFactory;
75use sp_api::{ApiExt, ConstructRuntimeApi, Metadata, ProvideRuntimeApi};
76use sp_block_builder::BlockBuilder;
77use sp_blockchain::HeaderMetadata;
78use sp_consensus::block_validation::DefaultBlockAnnounceValidator;
79use sp_consensus_slots::Slot;
80use sp_consensus_subspace::digests::extract_pre_digest;
81use sp_consensus_subspace::{
82 KzgExtension, PosExtension, PotExtension, PotNextSlotInput, SubspaceApi,
83};
84use sp_core::H256;
85use sp_core::offchain::OffchainDbExt;
86use sp_core::offchain::storage::OffchainDb;
87use sp_core::traits::SpawnEssentialNamed;
88use sp_domains::storage::StorageKey;
89use sp_domains::{BundleProducerElectionApi, DomainsApi};
90use sp_domains_fraud_proof::{FraudProofApi, FraudProofExtension, FraudProofHostFunctionsImpl};
91use sp_externalities::Extensions;
92use sp_messenger::MessengerApi;
93use sp_messenger_host_functions::{MessengerExtension, MessengerHostFunctionsImpl};
94use sp_mmr_primitives::MmrApi;
95use sp_objects::ObjectsApi;
96use sp_offchain::OffchainWorkerApi;
97use sp_runtime::traits::{Block as BlockT, BlockIdTo, Header, NumberFor, Zero};
98use sp_session::SessionKeys;
99use sp_subspace_mmr::host_functions::{SubspaceMmrExtension, SubspaceMmrHostFunctionsImpl};
100use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
101use static_assertions::const_assert;
102use std::marker::PhantomData;
103use std::num::NonZeroUsize;
104use std::sync::Arc;
105use std::time::Duration;
106use subspace_core_primitives::pieces::Record;
107use subspace_core_primitives::pot::PotSeed;
108use subspace_core_primitives::{BlockNumber, PublicKey, REWARD_SIGNING_CONTEXT};
109use subspace_erasure_coding::ErasureCoding;
110use subspace_kzg::Kzg;
111use subspace_networking::libp2p::multiaddr::Protocol;
112use subspace_networking::utils::piece_provider::PieceProvider;
113use subspace_proof_of_space::Table;
114use subspace_runtime_primitives::opaque::Block;
115use subspace_runtime_primitives::{AccountId, Balance, BlockHashFor, Hash, Nonce};
116use tokio::sync::broadcast;
117use tracing::{Instrument, debug, error, info};
118pub use utils::wait_for_block_import;
119
120const_assert!(std::mem::size_of::<usize>() >= std::mem::size_of::<u64>());
123
124const POT_VERIFIER_CACHE_SIZE: u32 = 30_000;
127const SYNC_TARGET_UPDATE_INTERVAL: Duration = Duration::from_secs(1);
128const PIECE_PROVIDER_MULTIPLIER: usize = 10;
130
131#[derive(thiserror::Error, Debug)]
133pub enum Error {
134 #[error(transparent)]
136 Io(#[from] std::io::Error),
137
138 #[error(transparent)]
140 AddrFormatInvalid(#[from] std::net::AddrParseError),
141
142 #[error(transparent)]
144 Sub(#[from] sc_service::Error),
145
146 #[error(transparent)]
148 Consensus(#[from] sp_consensus::Error),
149
150 #[error(transparent)]
152 Telemetry(#[from] sc_telemetry::Error),
153
154 #[error(transparent)]
156 SubspaceDsn(#[from] DsnConfigurationError),
157
158 #[error(transparent)]
160 BlockRelay(#[from] BlockRelayConfigurationError),
161
162 #[error(transparent)]
164 Other(Box<dyn std::error::Error + Send + Sync>),
165}
166
167#[derive(Clone)]
169struct BlockImportWrapper<BI>(BI);
170
171#[async_trait::async_trait]
172impl<Block, BI> BlockImport<Block> for BlockImportWrapper<BI>
173where
174 Block: BlockT,
175 BI: BlockImport<Block, Error = sc_consensus_subspace::block_import::Error<Block::Header>>
176 + Send
177 + Sync,
178{
179 type Error = sp_consensus::Error;
180
181 async fn check_block(
182 &self,
183 block: BlockCheckParams<Block>,
184 ) -> Result<ImportResult, Self::Error> {
185 self.0
186 .check_block(block)
187 .await
188 .map_err(|error| sp_consensus::Error::Other(error.into()))
189 }
190
191 async fn import_block(
192 &self,
193 block: BlockImportParams<Block>,
194 ) -> Result<ImportResult, Self::Error> {
195 self.0
196 .import_block(block)
197 .await
198 .map_err(|error| sp_consensus::Error::Other(error.into()))
199 }
200}
201
202#[cfg(not(feature = "runtime-benchmarks"))]
204pub type HostFunctions = (
205 sp_io::SubstrateHostFunctions,
206 sp_consensus_subspace::consensus::HostFunctions,
207 sp_domains_fraud_proof::HostFunctions,
208 sp_subspace_mmr::HostFunctions,
209 sp_messenger_host_functions::HostFunctions,
210);
211
212#[cfg(feature = "runtime-benchmarks")]
214pub type HostFunctions = (
215 sp_io::SubstrateHostFunctions,
216 frame_benchmarking::benchmarking::HostFunctions,
217 sp_consensus_subspace::consensus::HostFunctions,
218 sp_domains_fraud_proof::HostFunctions,
219 sp_subspace_mmr::HostFunctions,
220 sp_messenger_host_functions::HostFunctions,
221);
222
223pub type RuntimeExecutor = sc_executor::WasmExecutor<HostFunctions>;
225
226pub type FullClient<RuntimeApi> = sc_service::TFullClient<Block, RuntimeApi, RuntimeExecutor>;
228
229pub type FullBackend = sc_service::TFullBackend<Block>;
230pub type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
231
232struct SubspaceExtensionsFactory<PosTable, Client, DomainBlock> {
233 kzg: Kzg,
234 client: Arc<Client>,
235 backend: Arc<FullBackend>,
236 pot_verifier: PotVerifier,
237 domains_executor: Arc<sc_domains::RuntimeExecutor>,
238 confirmation_depth_k: BlockNumber,
239 _pos_table: PhantomData<(PosTable, DomainBlock)>,
240}
241
242impl<PosTable, Block, Client, DomainBlock> ExtensionsFactory<Block>
243 for SubspaceExtensionsFactory<PosTable, Client, DomainBlock>
244where
245 PosTable: Table,
246 Block: BlockT,
247 Block::Hash: From<H256> + Into<H256>,
248 DomainBlock: BlockT,
249 DomainBlock::Hash: Into<H256> + From<H256>,
250 Client: BlockBackend<Block>
251 + HeaderBackend<Block>
252 + ProvideRuntimeApi<Block>
253 + Send
254 + Sync
255 + 'static,
256 Client::Api: SubspaceApi<Block, PublicKey>
257 + DomainsApi<Block, DomainBlock::Header>
258 + BundleProducerElectionApi<Block, Balance>
259 + MmrApi<Block, H256, NumberFor<Block>>
260 + MessengerApi<Block, NumberFor<Block>, Block::Hash>,
261{
262 fn extensions_for(
263 &self,
264 _block_hash: Block::Hash,
265 _block_number: NumberFor<Block>,
266 ) -> Extensions {
267 let confirmation_depth_k = self.confirmation_depth_k;
268 let mut exts = Extensions::new();
269 exts.register(KzgExtension::new(self.kzg.clone()));
270 exts.register(PosExtension::new::<PosTable>());
271 exts.register(PotExtension::new({
272 let client = Arc::clone(&self.client);
273 let pot_verifier = self.pot_verifier.clone();
274
275 Box::new(
276 move |parent_hash, slot, proof_of_time, quick_verification| {
277 let parent_hash = {
278 let mut converted_parent_hash = Block::Hash::default();
279 converted_parent_hash.as_mut().copy_from_slice(&parent_hash);
280 converted_parent_hash
281 };
282
283 let parent_header = match client.header(parent_hash) {
284 Ok(Some(parent_header)) => parent_header,
285 Ok(None) => {
286 if quick_verification {
287 error!(
288 %parent_hash,
289 "Header not found during proof of time verification"
290 );
291
292 return false;
293 } else {
294 debug!(
295 %parent_hash,
296 "Header not found during proof of time verification"
297 );
298
299 return true;
302 }
303 }
304 Err(error) => {
305 error!(
306 %error,
307 %parent_hash,
308 "Failed to retrieve header during proof of time verification"
309 );
310
311 return false;
312 }
313 };
314
315 let parent_pre_digest = match extract_pre_digest(&parent_header) {
316 Ok(parent_pre_digest) => parent_pre_digest,
317 Err(error) => {
318 error!(
319 %error,
320 %parent_hash,
321 parent_number = %parent_header.number(),
322 "Failed to extract pre-digest from parent header during proof of \
323 time verification, this must never happen"
324 );
325
326 return false;
327 }
328 };
329
330 let parent_slot = parent_pre_digest.slot();
331 if slot <= *parent_slot {
332 return false;
333 }
334
335 let pot_parameters = match client.runtime_api().pot_parameters(parent_hash) {
336 Ok(pot_parameters) => pot_parameters,
337 Err(error) => {
338 debug!(
339 %error,
340 %parent_hash,
341 parent_number = %parent_header.number(),
342 "Failed to retrieve proof of time parameters during proof of time \
343 verification"
344 );
345
346 return false;
347 }
348 };
349
350 let pot_input = if parent_header.number().is_zero() {
351 PotNextSlotInput {
352 slot: parent_slot + Slot::from(1),
353 slot_iterations: pot_parameters.slot_iterations(),
354 seed: pot_verifier.genesis_seed(),
355 }
356 } else {
357 let pot_info = parent_pre_digest.pot_info();
358
359 PotNextSlotInput::derive(
360 pot_parameters.slot_iterations(),
361 parent_slot,
362 pot_info.proof_of_time(),
363 &pot_parameters.next_parameters_change(),
364 )
365 };
366
367 if quick_verification {
371 pot_verifier.try_is_output_valid(
372 pot_input,
373 Slot::from(slot) - parent_slot,
374 proof_of_time,
375 pot_parameters.next_parameters_change(),
376 )
377 } else {
378 pot_verifier.is_output_valid(
379 pot_input,
380 Slot::from(slot) - parent_slot,
381 proof_of_time,
382 pot_parameters.next_parameters_change(),
383 )
384 }
385 },
386 )
387 }));
388
389 exts.register(FraudProofExtension::new(Arc::new(
390 FraudProofHostFunctionsImpl::<_, _, DomainBlock, _, _>::new(
391 self.client.clone(),
392 self.domains_executor.clone(),
393 move |client, executor| {
394 let extension_factory =
395 DomainsExtensionFactory::<_, Block, DomainBlock, _>::new(
396 client,
397 executor,
398 confirmation_depth_k,
399 );
400 Box::new(extension_factory) as Box<dyn ExtensionsFactory<DomainBlock>>
401 },
402 ),
403 )));
404
405 exts.register(SubspaceMmrExtension::new(Arc::new(
406 SubspaceMmrHostFunctionsImpl::<Block, _>::new(
407 self.client.clone(),
408 confirmation_depth_k,
409 ),
410 )));
411
412 exts.register(MessengerExtension::new(Arc::new(
413 MessengerHostFunctionsImpl::<Block, _, DomainBlock, _>::new(
414 self.client.clone(),
415 self.domains_executor.clone(),
416 ),
417 )));
418
419 if let Some(offchain_storage) = self.backend.offchain_storage() {
422 let offchain_db = OffchainDb::new(offchain_storage);
423 exts.register(OffchainDbExt::new(offchain_db));
424 }
425
426 exts
427 }
428}
429
430pub struct OtherPartialComponents<RuntimeApi>
432where
433 RuntimeApi: ConstructRuntimeApi<Block, FullClient<RuntimeApi>> + Send + Sync + 'static,
434{
435 pub block_import: BoxBlockImport<Block>,
437 pub subspace_link: SubspaceLink<Block>,
439 pub segment_headers_store: SegmentHeadersStore<FullClient<RuntimeApi>>,
441 pub pot_verifier: PotVerifier,
443 pub sync_target_block_number: Arc<AtomicU32>,
445 pub telemetry: Option<Telemetry>,
447}
448
449type PartialComponents<RuntimeApi> = sc_service::PartialComponents<
450 FullClient<RuntimeApi>,
451 FullBackend,
452 FullSelectChain,
453 DefaultImportQueue<Block>,
454 TransactionPoolHandle<Block, FullClient<RuntimeApi>>,
455 OtherPartialComponents<RuntimeApi>,
456>;
457
458#[expect(clippy::result_large_err, reason = "Comes from Substrate")]
460pub fn new_partial<PosTable, RuntimeApi>(
461 config: &Configuration,
464 snap_sync: bool,
466 pot_external_entropy: &[u8],
467) -> Result<PartialComponents<RuntimeApi>, ServiceError>
468where
469 PosTable: Table,
470 RuntimeApi: ConstructRuntimeApi<Block, FullClient<RuntimeApi>> + Send + Sync + 'static,
471 RuntimeApi::RuntimeApi: ApiExt<Block>
472 + Metadata<Block>
473 + BlockBuilder<Block>
474 + OffchainWorkerApi<Block>
475 + SessionKeys<Block>
476 + TaggedTransactionQueue<Block>
477 + SubspaceApi<Block, PublicKey>
478 + DomainsApi<Block, DomainHeader>
479 + FraudProofApi<Block, DomainHeader>
480 + BundleProducerElectionApi<Block, Balance>
481 + ObjectsApi<Block>
482 + MmrApi<Block, H256, NumberFor<Block>>
483 + MessengerApi<Block, NumberFor<Block>, BlockHashFor<Block>>,
484{
485 let telemetry = config
486 .telemetry_endpoints
487 .clone()
488 .filter(|x| !x.is_empty())
489 .map(|endpoints| -> Result<_, sc_telemetry::Error> {
490 let worker = TelemetryWorker::new(16)?;
491 let telemetry = worker.handle().new_telemetry(endpoints);
492 Ok((worker, telemetry))
493 })
494 .transpose()?;
495
496 let executor = sc_service::new_wasm_executor(&config.executor);
497 let domains_executor = sc_service::new_wasm_executor(&config.executor);
498
499 let confirmation_depth_k = extract_confirmation_depth(config.chain_spec.as_ref()).ok_or(
500 ServiceError::Other("Failed to extract confirmation depth from chain spec".to_string()),
501 )?;
502
503 let backend = Arc::new(sc_client_db::Backend::new(
504 config.db_config(),
505 confirmation_depth_k.into(),
506 )?);
507
508 let genesis_block_builder = GenesisBlockBuilder::new(
509 config.chain_spec.as_storage_builder(),
510 !snap_sync,
511 backend.clone(),
512 executor.clone(),
513 )?;
514
515 let (client, backend, keystore_container, task_manager) =
516 sc_service::new_full_parts_with_genesis_builder::<Block, RuntimeApi, _, _>(
517 config,
518 telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
519 executor.clone(),
520 backend,
521 genesis_block_builder,
522 false,
523 )?;
524
525 let (kzg, maybe_erasure_coding) = tokio::task::block_in_place(|| {
527 rayon::join(Kzg::new, || {
528 ErasureCoding::new(
529 NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize)
530 .expect("Not zero; qed"),
531 )
532 .map_err(|error| format!("Failed to instantiate erasure coding: {error}"))
533 })
534 });
535 let erasure_coding = maybe_erasure_coding?;
536
537 let client = Arc::new(client);
538 let client_info = client.info();
539 let chain_constants = client
540 .runtime_api()
541 .chain_constants(client_info.best_hash)
542 .map_err(|error| ServiceError::Application(error.into()))?;
543
544 let pot_verifier = PotVerifier::new(
545 PotSeed::from_genesis(client_info.genesis_hash.as_ref(), pot_external_entropy),
546 POT_VERIFIER_CACHE_SIZE,
547 );
548
549 assert_eq!(confirmation_depth_k, chain_constants.confirmation_depth_k());
551
552 client
553 .execution_extensions()
554 .set_extensions_factory(SubspaceExtensionsFactory::<PosTable, _, DomainBlock> {
555 kzg: kzg.clone(),
556 client: Arc::clone(&client),
557 pot_verifier: pot_verifier.clone(),
558 domains_executor: Arc::new(domains_executor),
559 backend: backend.clone(),
560 confirmation_depth_k: chain_constants.confirmation_depth_k(),
561 _pos_table: PhantomData,
562 });
563
564 let telemetry = telemetry.map(|(worker, telemetry)| {
565 task_manager
566 .spawn_handle()
567 .spawn("telemetry", None, worker.run());
568 telemetry
569 });
570
571 let select_chain = sc_consensus::LongestChain::new(backend.clone());
572
573 let segment_headers_store = tokio::task::block_in_place(|| {
574 SegmentHeadersStore::new(client.clone(), chain_constants.confirmation_depth_k())
575 })
576 .map_err(|error| ServiceError::Application(error.into()))?;
577
578 let subspace_link = SubspaceLink::new(chain_constants, kzg.clone(), erasure_coding);
579 let segment_headers_store = segment_headers_store.clone();
580
581 let block_import = SubspaceBlockImport::<PosTable, _, _, _, _, _>::new(
582 client.clone(),
583 client.clone(),
584 subspace_link.clone(),
585 {
586 let client = client.clone();
587 let segment_headers_store = segment_headers_store.clone();
588
589 move |parent_hash, ()| {
590 let client = client.clone();
591 let segment_headers_store = segment_headers_store.clone();
592
593 async move {
594 let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
595
596 let parent_header = client
597 .header(parent_hash)?
598 .expect("Parent header must always exist when block is created; qed");
599
600 let parent_block_number = parent_header.number;
601
602 let subspace_inherents =
603 sp_consensus_subspace::inherents::InherentDataProvider::new(
604 segment_headers_store
605 .segment_headers_for_block(parent_block_number + 1),
606 );
607
608 Ok((timestamp, subspace_inherents))
609 }
610 }
611 },
612 segment_headers_store.clone(),
613 pot_verifier.clone(),
614 );
615
616 let sync_target_block_number = Arc::new(AtomicU32::new(0));
617 let transaction_pool = Arc::from(
618 sc_transaction_pool::Builder::new(
619 task_manager.spawn_essential_handle(),
620 client.clone(),
621 config.role.is_authority().into(),
622 )
623 .with_options(config.transaction_pool.clone())
624 .with_prometheus(config.prometheus_registry())
625 .build(),
626 );
627
628 let verifier = SubspaceVerifier::<PosTable, _, _>::new(SubspaceVerifierOptions {
629 client: client.clone(),
630 chain_constants,
631 kzg,
632 telemetry: telemetry.as_ref().map(|x| x.handle()),
633 reward_signing_context: schnorrkel::context::signing_context(REWARD_SIGNING_CONTEXT),
634 sync_target_block_number: Arc::clone(&sync_target_block_number),
635 is_authoring_blocks: config.role.is_authority(),
636 pot_verifier: pot_verifier.clone(),
637 });
638
639 let import_queue = BasicQueue::new(
640 verifier,
641 Box::new(BlockImportWrapper(block_import.clone())),
642 None,
643 &task_manager.spawn_essential_handle(),
644 config.prometheus_registry(),
645 );
646
647 let other = OtherPartialComponents {
648 block_import: Box::new(BlockImportWrapper(block_import.clone())),
649 subspace_link,
650 segment_headers_store,
651 pot_verifier,
652 sync_target_block_number,
653 telemetry,
654 };
655
656 Ok(PartialComponents {
657 client,
658 backend,
659 task_manager,
660 import_queue,
661 keystore_container,
662 select_chain,
663 transaction_pool,
664 other,
665 })
666}
667
668pub struct NewFull<Client>
670where
671 Client: ProvideRuntimeApi<Block>
672 + AuxStore
673 + BlockBackend<Block>
674 + BlockIdTo<Block>
675 + HeaderBackend<Block>
676 + HeaderMetadata<Block, Error = sp_blockchain::Error>
677 + 'static,
678 Client::Api: TaggedTransactionQueue<Block>
679 + DomainsApi<Block, DomainHeader>
680 + FraudProofApi<Block, DomainHeader>
681 + SubspaceApi<Block, PublicKey>
682 + MmrApi<Block, H256, NumberFor<Block>>
683 + MessengerApi<Block, NumberFor<Block>, BlockHashFor<Block>>,
684{
685 pub task_manager: TaskManager,
687 pub client: Arc<Client>,
689 pub select_chain: FullSelectChain,
691 pub network_service: Arc<dyn NetworkService + Send + Sync>,
693 pub xdm_gossip_notification_service: Box<dyn NotificationService>,
695 pub sync_service: Arc<sc_network_sync::SyncingService<Block>>,
697 pub rpc_handlers: sc_service::RpcHandlers,
699 pub backend: Arc<FullBackend>,
701 pub pot_slot_info_stream: broadcast::Receiver<PotSlotInfo>,
703 pub new_slot_notification_stream: SubspaceNotificationStream<NewSlotNotification>,
706 pub reward_signing_notification_stream: SubspaceNotificationStream<RewardSigningNotification>,
708 pub block_importing_notification_stream:
710 SubspaceNotificationStream<BlockImportingNotification<Block>>,
711 pub object_mapping_notification_stream: SubspaceNotificationStream<ObjectMappingNotification>,
713 pub archived_segment_notification_stream:
715 SubspaceNotificationStream<ArchivedSegmentNotification>,
716 pub transaction_pool: Arc<TransactionPoolHandle<Block, Client>>,
718}
719
720type FullNode<RuntimeApi> = NewFull<FullClient<RuntimeApi>>;
721
722#[expect(clippy::result_large_err, reason = "Comes from Substrate")]
724pub async fn new_full<PosTable, RuntimeApi>(
725 mut config: SubspaceConfiguration,
726 partial_components: PartialComponents<RuntimeApi>,
727 prometheus_registry: Option<&mut Registry>,
728 enable_rpc_extensions: bool,
729 block_proposal_slot_portion: SlotProportion,
730 consensus_snap_sync_target_block_receiver: Option<broadcast::Receiver<BlockNumber>>,
731) -> Result<FullNode<RuntimeApi>, Error>
732where
733 PosTable: Table,
734 RuntimeApi: ConstructRuntimeApi<Block, FullClient<RuntimeApi>> + Send + Sync + 'static,
735 RuntimeApi::RuntimeApi: ApiExt<Block>
736 + Metadata<Block>
737 + AccountNonceApi<Block, AccountId, Nonce>
738 + BlockBuilder<Block>
739 + OffchainWorkerApi<Block>
740 + SessionKeys<Block>
741 + TaggedTransactionQueue<Block>
742 + TransactionPaymentApi<Block, Balance>
743 + SubspaceApi<Block, PublicKey>
744 + DomainsApi<Block, DomainHeader>
745 + FraudProofApi<Block, DomainHeader>
746 + ObjectsApi<Block>
747 + MmrApi<Block, Hash, BlockNumber>
748 + MessengerApi<Block, NumberFor<Block>, BlockHashFor<Block>>,
749{
750 let PartialComponents {
751 client,
752 backend,
753 mut task_manager,
754 import_queue,
755 keystore_container,
756 select_chain,
757 transaction_pool,
758 other,
759 } = partial_components;
760 let OtherPartialComponents {
761 block_import,
762 subspace_link,
763 segment_headers_store,
764 pot_verifier,
765 sync_target_block_number,
766 mut telemetry,
767 } = other;
768
769 let offchain_indexing_enabled = config.base.offchain_worker.indexing_enabled;
770 let (node, bootstrap_nodes, piece_getter) = match config.subspace_networking {
771 SubspaceNetworking::Reuse {
772 node,
773 bootstrap_nodes,
774 piece_getter,
775 } => (node, bootstrap_nodes, piece_getter),
776 SubspaceNetworking::Create { config: dsn_config } => {
777 let dsn_protocol_version = hex::encode(client.chain_info().genesis_hash);
778
779 debug!(
780 chain_type=?config.base.chain_spec.chain_type(),
781 genesis_hash=%hex::encode(client.chain_info().genesis_hash),
782 "Setting DSN protocol version..."
783 );
784
785 let out_connections = dsn_config.max_out_connections;
786 let (node, mut node_runner) = create_dsn_instance(
787 dsn_protocol_version,
788 dsn_config.clone(),
789 prometheus_registry,
790 )?;
791
792 info!("Subspace networking initialized: Node ID is {}", node.id());
793
794 node.on_new_listener(Arc::new({
795 let node = node.clone();
796
797 move |address| {
798 info!(
799 "DSN listening on {}",
800 address.clone().with(Protocol::P2p(node.id()))
801 );
802 }
803 }))
804 .detach();
805
806 task_manager
807 .spawn_essential_handle()
808 .spawn_essential_blocking(
809 "node-runner",
810 Some("subspace-networking"),
811 Box::pin(
812 async move {
813 node_runner.run().await;
814 }
815 .in_current_span(),
816 ),
817 );
818
819 let piece_provider = PieceProvider::new(
820 node.clone(),
821 SegmentCommitmentPieceValidator::new(
822 node.clone(),
823 subspace_link.kzg().clone(),
824 segment_headers_store.clone(),
825 ),
826 Arc::new(Semaphore::new(
827 out_connections as usize * PIECE_PROVIDER_MULTIPLIER,
828 )),
829 );
830
831 (
832 node,
833 dsn_config.bootstrap_nodes,
834 Arc::new(DsnPieceGetter::new(piece_provider)) as _,
835 )
836 }
837 };
838
839 let dsn_bootstrap_nodes = {
840 if bootstrap_nodes.is_empty() {
843 let (node_address_sender, node_address_receiver) = oneshot::channel();
844 let _handler = node.on_new_listener(Arc::new({
845 let node_address_sender = Mutex::new(Some(node_address_sender));
846
847 move |address| {
848 if matches!(address.iter().next(), Some(Protocol::Ip4(_)))
849 && let Some(node_address_sender) = node_address_sender.lock().take()
850 && let Err(err) = node_address_sender.send(address.clone())
851 {
852 debug!(?err, "Couldn't send a node address to the channel.");
853 }
854 }
855 }));
856
857 let mut node_listeners = node.listeners();
858
859 if node_listeners.is_empty() {
860 let Ok(listener) = node_address_receiver.await else {
861 return Err(Error::Other(
862 "Oneshot receiver dropped before DSN node listener was ready"
863 .to_string()
864 .into(),
865 ));
866 };
867
868 node_listeners = vec![listener];
869 }
870
871 node_listeners.iter_mut().for_each(|multiaddr| {
872 multiaddr.push(Protocol::P2p(node.id()));
873 });
874
875 node_listeners
876 } else {
877 bootstrap_nodes
878 }
879 };
880
881 let substrate_prometheus_registry = config
882 .base
883 .prometheus_config
884 .as_ref()
885 .map(|prometheus_config| prometheus_config.registry.clone());
886 let import_queue_service1 = import_queue.service();
887 let import_queue_service2 = import_queue.service();
888 let network_wrapper = Arc::new(NetworkWrapper::default());
889 let block_relay = build_consensus_relay(
890 network_wrapper.clone(),
891 client.clone(),
892 transaction_pool.clone(),
893 substrate_prometheus_registry.as_ref(),
894 )
895 .map_err(Error::BlockRelay)?;
896 let mut net_config = sc_network::config::FullNetworkConfiguration::new(
897 &config.base.network,
898 substrate_prometheus_registry.clone(),
899 );
900 let (xdm_gossip_notification_config, xdm_gossip_notification_service) =
901 xdm_gossip_peers_set_config();
902 net_config.add_notification_protocol(xdm_gossip_notification_config);
903 let (pot_gossip_notification_config, pot_gossip_notification_service) =
904 pot_gossip_peers_set_config();
905 net_config.add_notification_protocol(pot_gossip_notification_config);
906 let pause_sync = Arc::clone(&net_config.network_config.pause_sync);
907
908 let num_peer_hint = net_config.network_config.default_peers_set_num_full as usize
909 + net_config
910 .network_config
911 .default_peers_set
912 .reserved_nodes
913 .len();
914
915 let protocol_id = config.base.protocol_id();
916 let fork_id = config.base.chain_spec.fork_id();
917
918 let (handler, protocol_config) = DomainBlockERRequestHandler::new::<
920 NetworkWorker<Block, BlockHashFor<Block>>,
921 >(fork_id, client.clone(), num_peer_hint);
922 task_manager.spawn_handle().spawn(
923 "domain-block-er-request-handler",
924 Some("networking"),
925 handler.run(),
926 );
927 net_config.add_request_response_protocol(protocol_config);
928
929 if let Some(offchain_storage) = backend.offchain_storage() {
930 let (handler, protocol_config) =
932 MmrRequestHandler::new::<NetworkWorker<Block, BlockHashFor<Block>>>(
933 &config.base.protocol_id(),
934 fork_id,
935 client.clone(),
936 num_peer_hint,
937 offchain_storage,
938 );
939 task_manager
940 .spawn_handle()
941 .spawn("mmr-request-handler", Some("networking"), handler.run());
942
943 net_config.add_request_response_protocol(protocol_config);
944 }
945
946 let network_service_provider = NetworkServiceProvider::new();
947 let network_service_handle = network_service_provider.handle();
948 let (network_service, system_rpc_tx, tx_handler_controller, sync_service) = {
949 let spawn_handle = task_manager.spawn_handle();
950 let metrics = NotificationMetrics::new(substrate_prometheus_registry.as_ref());
951
952 let block_downloader = {
954 let BlockRelayParams {
955 mut server,
956 downloader,
957 request_response_config,
958 } = block_relay;
959 net_config.add_request_response_protocol(request_response_config);
960
961 spawn_handle.spawn("block-request-handler", Some("networking"), async move {
962 server.run().await;
963 });
964
965 downloader
966 };
967
968 let syncing_strategy = build_polkadot_syncing_strategy(
969 protocol_id.clone(),
970 fork_id,
971 &mut net_config,
972 None,
973 block_downloader,
974 client.clone(),
975 &spawn_handle,
976 substrate_prometheus_registry.as_ref(),
977 )?;
978
979 let (syncing_engine, sync_service, block_announce_config) =
980 SyncingEngine::new::<NetworkWorker<_, _>>(
981 Roles::from(&config.base.role),
982 Arc::clone(&client),
983 substrate_prometheus_registry.as_ref(),
984 metrics.clone(),
985 &net_config,
986 protocol_id.clone(),
987 fork_id,
988 Box::new(DefaultBlockAnnounceValidator),
989 syncing_strategy,
990 network_service_provider.handle(),
991 import_queue.service(),
992 net_config.peer_store_handle(),
993 config.base.network.force_synced,
994 )
995 .map_err(sc_service::Error::from)?;
996
997 spawn_handle.spawn_blocking("syncing", None, syncing_engine.run());
998
999 build_network_advanced(BuildNetworkAdvancedParams {
1000 role: config.base.role,
1001 protocol_id,
1002 fork_id,
1003 ipfs_server: config.base.network.ipfs_server,
1004 announce_block: config.base.announce_block,
1005 net_config,
1006 client: Arc::clone(&client),
1007 transaction_pool: Arc::clone(&transaction_pool),
1008 spawn_handle,
1009 import_queue,
1010 sync_service,
1011 block_announce_config,
1012 network_service_provider,
1013 metrics_registry: substrate_prometheus_registry.as_ref(),
1014 metrics,
1015 })?
1016 };
1017
1018 task_manager.spawn_handle().spawn(
1019 "sync-target-follower",
1020 None,
1021 Box::pin({
1022 let sync_service = sync_service.clone();
1023 let sync_target_block_number = Arc::clone(&sync_target_block_number);
1024
1025 async move {
1026 loop {
1027 let best_seen_block = sync_service
1028 .status()
1029 .await
1030 .map(|status| status.best_seen_block.unwrap_or_default())
1031 .unwrap_or_default();
1032 sync_target_block_number.store(best_seen_block, Ordering::Relaxed);
1033
1034 tokio::time::sleep(SYNC_TARGET_UPDATE_INTERVAL).await;
1035 }
1036 }
1037 }),
1038 );
1039
1040 let sync_oracle = SubspaceSyncOracle::new(
1041 config.base.force_authoring,
1042 Arc::clone(&pause_sync),
1043 sync_service.clone(),
1044 );
1045
1046 let subspace_archiver = tokio::task::block_in_place(|| {
1047 create_subspace_archiver(
1048 segment_headers_store.clone(),
1049 subspace_link.clone(),
1050 client.clone(),
1051 backend.clone(),
1052 sync_oracle.clone(),
1053 telemetry.as_ref().map(|telemetry| telemetry.handle()),
1054 config.create_object_mappings,
1055 )
1056 })
1057 .map_err(ServiceError::Client)?;
1058
1059 task_manager
1060 .spawn_essential_handle()
1061 .spawn_essential_blocking(
1062 "subspace-archiver",
1063 None,
1064 Box::pin(async move {
1065 if let Err(error) = subspace_archiver.await {
1066 error!(%error, "Archiver exited with error");
1067 }
1068 }),
1069 );
1070
1071 network_wrapper.set(network_service.clone());
1072
1073 if !config.base.network.force_synced {
1074 pause_sync.store(true, Ordering::Release);
1076 }
1077
1078 let snap_sync_task = snap_sync(
1079 segment_headers_store.clone(),
1080 node.clone(),
1081 fork_id.map(|fork_id| fork_id.to_string()),
1082 Arc::clone(&client),
1083 import_queue_service1,
1084 pause_sync.clone(),
1085 piece_getter.clone(),
1086 sync_service.clone(),
1087 network_service_handle,
1088 subspace_link.erasure_coding().clone(),
1089 consensus_snap_sync_target_block_receiver,
1090 backend.offchain_storage(),
1091 network_service.clone(),
1092 );
1093
1094 let (observer, worker) = sync_from_dsn::create_observer_and_worker(
1095 segment_headers_store.clone(),
1096 Arc::clone(&network_service),
1097 node.clone(),
1098 Arc::clone(&client),
1099 import_queue_service2,
1100 sync_service.clone(),
1101 sync_target_block_number,
1102 pause_sync,
1103 piece_getter,
1104 subspace_link.erasure_coding().clone(),
1105 );
1106 task_manager
1107 .spawn_handle()
1108 .spawn("observer", Some("sync-from-dsn"), observer);
1109 task_manager
1110 .spawn_essential_handle()
1111 .spawn_essential_blocking(
1112 "worker",
1113 Some("sync-from-dsn"),
1114 Box::pin(async move {
1115 if config.sync == ChainSyncMode::Snap
1117 && let Err(error) = snap_sync_task.in_current_span().await
1118 {
1119 error!(%error, "Snap sync exited with a fatal error");
1120 return;
1121 }
1122
1123 if let Err(error) = worker.await {
1124 error!(%error, "Sync from DSN exited with an error");
1125 }
1126 }),
1127 );
1128
1129 if let Some(registry) = substrate_prometheus_registry.as_ref() {
1130 match NodeMetrics::new(
1131 client.clone(),
1132 client.every_import_notification_stream(),
1133 registry,
1134 ) {
1135 Ok(node_metrics) => {
1136 task_manager.spawn_handle().spawn(
1137 "node_metrics",
1138 None,
1139 Box::pin(async move {
1140 node_metrics.run().await;
1141 }),
1142 );
1143 }
1144 Err(err) => {
1145 error!("Failed to initialize node metrics: {err:?}");
1146 }
1147 }
1148 }
1149
1150 let offchain_tx_pool_factory = OffchainTransactionPoolFactory::new(transaction_pool.clone());
1151
1152 if config.base.offchain_worker.enabled {
1153 let offchain_workers =
1154 sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions {
1155 runtime_api_provider: client.clone(),
1156 is_validator: config.base.role.is_authority(),
1157 keystore: Some(keystore_container.keystore()),
1158 offchain_db: backend.offchain_storage(),
1159 transaction_pool: Some(offchain_tx_pool_factory.clone()),
1160 network_provider: Arc::new(network_service.clone()),
1161 enable_http_requests: true,
1162 custom_extensions: |_| vec![],
1163 })?;
1164 task_manager.spawn_handle().spawn(
1165 "offchain-workers-runner",
1166 "offchain-worker",
1167 offchain_workers
1168 .run(client.clone(), task_manager.spawn_handle())
1169 .boxed(),
1170 );
1171 }
1172
1173 if offchain_indexing_enabled {
1175 task_manager.spawn_essential_handle().spawn_blocking(
1176 "mmr-gadget",
1177 None,
1178 mmr_gadget::MmrGadget::start(
1179 client.clone(),
1180 backend.clone(),
1181 sp_mmr_primitives::INDEXING_PREFIX.to_vec(),
1182 ),
1183 );
1184 }
1185
1186 let backoff_authoring_blocks: Option<()> = None;
1187
1188 let new_slot_notification_stream = subspace_link.new_slot_notification_stream();
1189 let reward_signing_notification_stream = subspace_link.reward_signing_notification_stream();
1190 let block_importing_notification_stream = subspace_link.block_importing_notification_stream();
1191 let object_mapping_notification_stream = subspace_link.object_mapping_notification_stream();
1192 let archived_segment_notification_stream = subspace_link.archived_segment_notification_stream();
1193
1194 let (pot_source_worker, pot_gossip_worker, pot_slot_info_stream) = PotSourceWorker::new(
1195 config.is_timekeeper,
1196 config.timekeeper_cpu_cores,
1197 client.clone(),
1198 pot_verifier.clone(),
1199 Arc::clone(&network_service),
1200 pot_gossip_notification_service,
1201 sync_service.clone(),
1202 sync_oracle.clone(),
1203 )
1204 .map_err(|error| Error::Other(error.into()))?;
1205
1206 let additional_pot_slot_info_stream = pot_source_worker.subscribe_pot_slot_info_stream();
1207
1208 task_manager
1209 .spawn_essential_handle()
1210 .spawn("pot-source", Some("pot"), pot_source_worker.run());
1211 task_manager
1212 .spawn_essential_handle()
1213 .spawn("pot-gossip", Some("pot"), pot_gossip_worker.run());
1214
1215 if config.base.role.is_authority() || config.force_new_slot_notifications {
1216 let proposer_factory = ProposerFactory::new(
1217 task_manager.spawn_handle(),
1218 client.clone(),
1219 transaction_pool.clone(),
1220 substrate_prometheus_registry.as_ref(),
1221 telemetry.as_ref().map(|x| x.handle()),
1222 );
1223
1224 let subspace_slot_worker =
1225 SubspaceSlotWorker::<PosTable, _, _, _, _, _, _, _>::new(SubspaceSlotWorkerOptions {
1226 client: client.clone(),
1227 env: proposer_factory,
1228 block_import,
1229 sync_oracle: sync_oracle.clone(),
1230 justification_sync_link: sync_service.clone(),
1231 force_authoring: config.base.force_authoring,
1232 backoff_authoring_blocks,
1233 subspace_link: subspace_link.clone(),
1234 segment_headers_store: segment_headers_store.clone(),
1235 block_proposal_slot_portion,
1236 max_block_proposal_slot_portion: None,
1237 telemetry: telemetry.as_ref().map(|x| x.handle()),
1238 offchain_tx_pool_factory,
1239 pot_verifier,
1240 });
1241
1242 let create_inherent_data_providers = {
1243 let client = client.clone();
1244 let segment_headers_store = segment_headers_store.clone();
1245
1246 move |parent_hash, ()| {
1247 let client = client.clone();
1248 let segment_headers_store = segment_headers_store.clone();
1249
1250 async move {
1251 let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
1252
1253 let parent_header = client
1254 .header(parent_hash)?
1255 .expect("Parent header must always exist when block is created; qed");
1256
1257 let parent_block_number = parent_header.number;
1258
1259 let subspace_inherents =
1260 sp_consensus_subspace::inherents::InherentDataProvider::new(
1261 segment_headers_store
1262 .segment_headers_for_block(parent_block_number + 1),
1263 );
1264
1265 Ok((timestamp, subspace_inherents))
1266 }
1267 }
1268 };
1269
1270 info!("🧑🌾 Starting Subspace Authorship worker");
1271 let slot_worker_task = sc_proof_of_time::start_slot_worker(
1272 subspace_link.chain_constants().slot_duration(),
1273 client.clone(),
1274 select_chain.clone(),
1275 subspace_slot_worker,
1276 sync_oracle.clone(),
1277 create_inherent_data_providers,
1278 pot_slot_info_stream,
1279 );
1280
1281 task_manager.spawn_essential_handle().spawn_blocking(
1284 "subspace-proposer",
1285 Some("block-authoring"),
1286 slot_worker_task,
1287 );
1288 }
1289
1290 config.base.prometheus_config.take();
1292
1293 let rpc_handlers = task_spawner::spawn_tasks(SpawnTasksParams {
1294 network: network_service.clone(),
1295 client: client.clone(),
1296 keystore: keystore_container.keystore(),
1297 task_manager: &mut task_manager,
1298 transaction_pool: transaction_pool.clone(),
1299 rpc_builder: if enable_rpc_extensions {
1300 let client = client.clone();
1301 let new_slot_notification_stream = new_slot_notification_stream.clone();
1302 let reward_signing_notification_stream = reward_signing_notification_stream.clone();
1303 let object_mapping_notification_stream = object_mapping_notification_stream.clone();
1304 let archived_segment_notification_stream = archived_segment_notification_stream.clone();
1305 let transaction_pool = transaction_pool.clone();
1306 let backend = backend.clone();
1307
1308 #[allow(clippy::result_large_err)]
1309 Box::new(move |subscription_executor| {
1310 let deps = rpc::FullDeps {
1311 client: client.clone(),
1312 pool: transaction_pool.clone(),
1313 subscription_executor,
1314 new_slot_notification_stream: new_slot_notification_stream.clone(),
1315 reward_signing_notification_stream: reward_signing_notification_stream.clone(),
1316 object_mapping_notification_stream: object_mapping_notification_stream.clone(),
1317 archived_segment_notification_stream: archived_segment_notification_stream
1318 .clone(),
1319 dsn_bootstrap_nodes: dsn_bootstrap_nodes.clone(),
1320 segment_headers_store: segment_headers_store.clone(),
1321 sync_oracle: sync_oracle.clone(),
1322 kzg: subspace_link.kzg().clone(),
1323 erasure_coding: subspace_link.erasure_coding().clone(),
1324 backend: backend.clone(),
1325 };
1326
1327 rpc::create_full(deps).map_err(Into::into)
1328 })
1329 } else {
1330 #[allow(clippy::result_large_err)]
1331 Box::new(|_| Ok(RpcModule::new(())))
1332 },
1333 backend: backend.clone(),
1334 system_rpc_tx,
1335 config: config.base,
1336 telemetry: telemetry.as_mut(),
1337 tx_handler_controller,
1338 sync_service: sync_service.clone(),
1339 tracing_execute_block: None,
1341 })?;
1342
1343 Ok(NewFull {
1344 task_manager,
1345 client,
1346 select_chain,
1347 network_service,
1348 xdm_gossip_notification_service,
1349 sync_service,
1350 rpc_handlers,
1351 backend,
1352 pot_slot_info_stream: additional_pot_slot_info_stream,
1353 new_slot_notification_stream,
1354 reward_signing_notification_stream,
1355 block_importing_notification_stream,
1356 object_mapping_notification_stream,
1357 archived_segment_notification_stream,
1358 transaction_pool,
1359 })
1360}
1361
1362fn confirmation_depth_storage_key() -> StorageKey {
1364 StorageKey(
1365 frame_support::storage::storage_prefix(
1366 "RuntimeConfigs".as_bytes(),
1369 "ConfirmationDepthK".as_bytes(),
1371 )
1372 .to_vec(),
1373 )
1374}
1375
1376fn extract_confirmation_depth(chain_spec: &dyn ChainSpec) -> Option<u32> {
1377 let storage_key = format!("0x{}", hex::encode(confirmation_depth_storage_key().0));
1378 let spec: serde_json::Value =
1379 serde_json::from_str(chain_spec.as_json(true).ok()?.as_str()).ok()?;
1380 let encoded_confirmation_depth = hex::decode(
1381 spec.pointer(format!("/genesis/raw/top/{storage_key}").as_str())?
1382 .as_str()?
1383 .trim_start_matches("0x"),
1384 )
1385 .ok()?;
1386 u32::decode(&mut encoded_confirmation_depth.as_slice()).ok()
1387}
1388
1389#[cfg(test)]
1390mod test {
1391 use static_assertions::const_assert_eq;
1392 use subspace_data_retrieval::object_fetcher::MAX_BLOCK_LENGTH as ARCHIVER_MAX_BLOCK_LENGTH;
1393 use subspace_runtime_primitives::MAX_BLOCK_LENGTH as CONSENSUS_RUNTIME_MAX_BLOCK_LENGTH;
1394
1395 #[test]
1398 fn max_block_length_consistent() {
1399 const_assert_eq!(
1400 CONSENSUS_RUNTIME_MAX_BLOCK_LENGTH,
1401 ARCHIVER_MAX_BLOCK_LENGTH,
1402 );
1403 }
1404}