@@ -15,6 +15,7 @@ use cumulus_client_cli::CollatorOptions;
1515use cumulus_client_collator:: service:: CollatorService ;
1616use cumulus_client_consensus_aura:: collators:: lookahead:: { self as aura, Params as AuraParams } ;
1717use cumulus_client_consensus_common:: ParachainBlockImport as TParachainBlockImport ;
18+ use cumulus_client_parachain_inherent:: MockValidationDataInherentDataProvider ;
1819use cumulus_client_service:: {
1920 build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks,
2021 BuildNetworkParams , CollatorSybilResistance , DARecoveryProfile , ParachainHostFunctions ,
@@ -25,18 +26,24 @@ use cumulus_primitives_core::{
2526 ParaId ,
2627} ;
2728use cumulus_relay_chain_interface:: { OverseerHandle , RelayChainInterface } ;
29+ use polkadot_primitives:: { HeadData , UpgradeGoAhead } ;
2830
2931// Substrate Imports
32+ use codec:: Encode ;
33+ use cumulus_primitives_core:: CollectCollationInfo ;
3034use frame_benchmarking_cli:: SUBSTRATE_REFERENCE_HARDWARE ;
3135use prometheus_endpoint:: Registry ;
3236use sc_client_api:: Backend ;
33- use sc_consensus:: ImportQueue ;
37+ use sc_consensus:: { ImportQueue , LongestChain } ;
38+ use sc_consensus_manual_seal:: consensus:: aura:: AuraConsensusDataProvider ;
3439use sc_executor:: { HeapAllocStrategy , WasmExecutor , DEFAULT_HEAP_ALLOC_STRATEGY } ;
3540use sc_network:: { NetworkBackend , NetworkBlock } ;
3641use sc_service:: { Configuration , PartialComponents , TFullBackend , TFullClient , TaskManager } ;
3742use sc_telemetry:: { Telemetry , TelemetryHandle , TelemetryWorker , TelemetryWorkerHandle } ;
3843use sc_transaction_pool_api:: OffchainTransactionPoolFactory ;
44+ use sp_api:: ProvideRuntimeApi ;
3945use sp_keystore:: KeystorePtr ;
46+ use sp_runtime:: traits:: UniqueSaturatedInto ;
4047
4148type ParachainExecutor = WasmExecutor < ParachainHostFunctions > ;
4249
@@ -406,3 +413,266 @@ pub async fn start_parachain_node(
406413
407414 Ok ( ( task_manager, client) )
408415}
416+
417+ /// Creates the inherent data providers for dev mode (instant/manual seal).
418+ ///
419+ /// This function sets up the timestamp and parachain validation data providers
420+ /// required for dev seal block production in a parachain environment without a relay chain.
421+ fn create_dev_inherent_data_providers (
422+ client : Arc < ParachainClient > ,
423+ para_id : ParaId ,
424+ slot_duration : sp_consensus_aura:: SlotDuration ,
425+ ) -> impl Fn (
426+ Hash ,
427+ ( ) ,
428+ ) -> std:: future:: Ready <
429+ Result <
430+ ( sp_timestamp:: InherentDataProvider , MockValidationDataInherentDataProvider < ( ) > ) ,
431+ Box < dyn std:: error:: Error + Send + Sync > ,
432+ > ,
433+ > + Send
434+ + Sync {
435+ move |parent_hash : Hash , ( ) | {
436+ let current_para_head = client
437+ . header ( parent_hash)
438+ . expect ( "Header lookup should succeed" )
439+ . expect ( "Header passed in as parent should be present in backend." ) ;
440+
441+ // Check if there's pending validation code that needs upgrade signal
442+ let should_send_go_ahead = client
443+ . runtime_api ( )
444+ . collect_collation_info ( parent_hash, & current_para_head)
445+ . map ( |info| info. new_validation_code . is_some ( ) )
446+ . unwrap_or_default ( ) ;
447+
448+ let current_para_block_head = Some ( HeadData ( current_para_head. encode ( ) ) ) ;
449+ let current_block_number =
450+ UniqueSaturatedInto :: < u32 > :: unique_saturated_into ( current_para_head. number ) + 1 ;
451+
452+ // Create mock parachain validation data
453+ let mocked_parachain = MockValidationDataInherentDataProvider :: < ( ) > {
454+ current_para_block : current_block_number,
455+ para_id,
456+ current_para_block_head,
457+ relay_blocks_per_para_block : 1 ,
458+ para_blocks_per_relay_epoch : 10 ,
459+ upgrade_go_ahead : should_send_go_ahead. then ( || {
460+ log:: info!( "Detected pending validation code, sending go-ahead signal." ) ;
461+ UpgradeGoAhead :: GoAhead
462+ } ) ,
463+ ..Default :: default ( )
464+ } ;
465+
466+ // Create timestamp aligned to Aura slot timing
467+ let timestamp = sp_timestamp:: InherentDataProvider :: new (
468+ ( slot_duration. as_millis ( ) * current_block_number as u64 ) . into ( ) ,
469+ ) ;
470+
471+ std:: future:: ready ( Ok ( ( timestamp, mocked_parachain) ) )
472+ }
473+ }
474+
475+ /// Start a parachain node in dev mode with instant seal.
476+ ///
477+ /// This allows the parachain to run standalone without connecting to a relay chain,
478+ /// useful for development and testing.
479+ pub async fn start_dev_parachain_node (
480+ mut config : Configuration ,
481+ finalize_delay_sec : u64 ,
482+ ) -> sc_service:: error:: Result < TaskManager > {
483+ // Since this is a dev node, prevent it from connecting to peers
484+ config. network . default_peers_set . in_peers = 0 ;
485+ config. network . default_peers_set . out_peers = 0 ;
486+
487+ // Build client, backend, and other components manually
488+ // We can't use new_partial() because it creates an Aura import queue
489+ let telemetry = config
490+ . telemetry_endpoints
491+ . clone ( )
492+ . filter ( |x| !x. is_empty ( ) )
493+ . map ( |endpoints| -> Result < _ , sc_telemetry:: Error > {
494+ let worker = TelemetryWorker :: new ( 16 ) ?;
495+ let telemetry = worker. handle ( ) . new_telemetry ( endpoints) ;
496+ Ok ( ( worker, telemetry) )
497+ } )
498+ . transpose ( ) ?;
499+
500+ let heap_pages = config
501+ . executor
502+ . default_heap_pages
503+ . map_or ( DEFAULT_HEAP_ALLOC_STRATEGY , |h| HeapAllocStrategy :: Static { extra_pages : h as _ } ) ;
504+
505+ let executor = ParachainExecutor :: builder ( )
506+ . with_execution_method ( config. executor . wasm_method )
507+ . with_onchain_heap_alloc_strategy ( heap_pages)
508+ . with_offchain_heap_alloc_strategy ( heap_pages)
509+ . with_max_runtime_instances ( config. executor . max_runtime_instances )
510+ . with_runtime_cache_size ( config. executor . runtime_cache_size )
511+ . build ( ) ;
512+
513+ let ( client, backend, keystore_container, mut task_manager) =
514+ sc_service:: new_full_parts_record_import :: < Block , RuntimeApi , _ > (
515+ & config,
516+ telemetry. as_ref ( ) . map ( |( _, telemetry) | telemetry. handle ( ) ) ,
517+ executor,
518+ true ,
519+ ) ?;
520+ let client = Arc :: new ( client) ;
521+
522+ let mut telemetry = telemetry. map ( |( worker, telemetry) | {
523+ task_manager. spawn_handle ( ) . spawn ( "telemetry" , None , worker. run ( ) ) ;
524+ telemetry
525+ } ) ;
526+
527+ let transaction_pool = Arc :: from (
528+ sc_transaction_pool:: Builder :: new (
529+ task_manager. spawn_essential_handle ( ) ,
530+ client. clone ( ) ,
531+ config. role . is_authority ( ) . into ( ) ,
532+ )
533+ . with_options ( config. transaction_pool . clone ( ) )
534+ . with_prometheus ( config. prometheus_registry ( ) )
535+ . build ( ) ,
536+ ) ;
537+
538+ // Create manual seal import queue (accepts all blocks without relay chain validation)
539+ let import_queue = sc_consensus_manual_seal:: import_queue (
540+ Box :: new ( client. clone ( ) ) ,
541+ & task_manager. spawn_essential_handle ( ) ,
542+ config. prometheus_registry ( ) ,
543+ ) ;
544+
545+ let net_config = sc_network:: config:: FullNetworkConfiguration :: <
546+ _ ,
547+ _ ,
548+ sc_network:: NetworkWorker < Block , Hash > ,
549+ > :: new (
550+ & config. network ,
551+ config. prometheus_config . as_ref ( ) . map ( |cfg| cfg. registry . clone ( ) ) ,
552+ ) ;
553+
554+ let ( network, system_rpc_tx, tx_handler_controller, sync_service) =
555+ sc_service:: build_network ( sc_service:: BuildNetworkParams {
556+ config : & config,
557+ client : client. clone ( ) ,
558+ transaction_pool : transaction_pool. clone ( ) ,
559+ spawn_handle : task_manager. spawn_handle ( ) ,
560+ import_queue,
561+ net_config,
562+ block_announce_validator_builder : None ,
563+ warp_sync_config : None ,
564+ block_relay : None ,
565+ metrics : sc_network:: NetworkWorker :: < Block , Hash > :: register_notification_metrics (
566+ config. prometheus_config . as_ref ( ) . map ( |config| & config. registry ) ,
567+ ) ,
568+ } ) ?;
569+
570+ if config. offchain_worker . enabled {
571+ use futures:: FutureExt ;
572+
573+ let offchain_workers =
574+ sc_offchain:: OffchainWorkers :: new ( sc_offchain:: OffchainWorkerOptions {
575+ runtime_api_provider : client. clone ( ) ,
576+ keystore : Some ( keystore_container. keystore ( ) ) ,
577+ offchain_db : backend. offchain_storage ( ) ,
578+ transaction_pool : Some ( OffchainTransactionPoolFactory :: new (
579+ transaction_pool. clone ( ) ,
580+ ) ) ,
581+ network_provider : Arc :: new ( network. clone ( ) ) ,
582+ is_validator : config. role . is_authority ( ) ,
583+ enable_http_requests : true ,
584+ custom_extensions : move |_| vec ! [ ] ,
585+ } ) ?;
586+ task_manager. spawn_handle ( ) . spawn (
587+ "offchain-workers-runner" ,
588+ "offchain-work" ,
589+ offchain_workers. run ( client. clone ( ) , task_manager. spawn_handle ( ) ) . boxed ( ) ,
590+ ) ;
591+ }
592+
593+ let proposer = sc_basic_authorship:: ProposerFactory :: new (
594+ task_manager. spawn_handle ( ) ,
595+ client. clone ( ) ,
596+ transaction_pool. clone ( ) ,
597+ None ,
598+ None ,
599+ ) ;
600+
601+ // Get slot duration from runtime
602+ let slot_duration = sc_consensus_aura:: slot_duration ( & * client)
603+ . expect ( "Slot duration is always present in Aura runtime; qed." ) ;
604+
605+ // The aura digest provider will provide digests that match the provided timestamp data.
606+ // Without this, the AURA parachain runtime complains about slot mismatches.
607+ let aura_digest_provider = AuraConsensusDataProvider :: new_with_slot_duration ( slot_duration) ;
608+
609+ // Extract para_id from chain spec
610+ let para_id = crate :: chain_spec:: Extensions :: try_get ( & * config. chain_spec )
611+ . map ( |e| e. para_id )
612+ . ok_or ( "Could not find parachain ID in chain-spec." ) ?;
613+ let para_id = ParaId :: from ( para_id) ;
614+
615+ // Create inherent data providers with mocked relay chain data
616+ let create_inherent_data_providers =
617+ create_dev_inherent_data_providers ( client. clone ( ) , para_id, slot_duration) ;
618+
619+ // Spawn instant seal consensus
620+ let params = sc_consensus_manual_seal:: InstantSealParams {
621+ block_import : client. clone ( ) ,
622+ env : proposer,
623+ client : client. clone ( ) ,
624+ pool : transaction_pool. clone ( ) ,
625+ select_chain : LongestChain :: new ( backend. clone ( ) ) ,
626+ consensus_data_provider : Some ( Box :: new ( aura_digest_provider) ) ,
627+ create_inherent_data_providers,
628+ } ;
629+
630+ let authorship_future = sc_consensus_manual_seal:: run_instant_seal ( params) ;
631+ task_manager
632+ . spawn_essential_handle ( )
633+ . spawn_blocking ( "instant-seal" , None , authorship_future) ;
634+
635+ // Optional delayed finalization
636+ if finalize_delay_sec > 0 {
637+ let delayed_finalize_params = sc_consensus_manual_seal:: DelayedFinalizeParams {
638+ client : client. clone ( ) ,
639+ spawn_handle : task_manager. spawn_handle ( ) ,
640+ delay_sec : finalize_delay_sec,
641+ } ;
642+ task_manager. spawn_essential_handle ( ) . spawn_blocking (
643+ "delayed_finalize" ,
644+ None ,
645+ sc_consensus_manual_seal:: run_delayed_finalize ( delayed_finalize_params) ,
646+ ) ;
647+ }
648+
649+ let rpc_builder = {
650+ let client = client. clone ( ) ;
651+ let transaction_pool = transaction_pool. clone ( ) ;
652+
653+ Box :: new ( move |_| {
654+ let deps =
655+ crate :: rpc:: FullDeps { client : client. clone ( ) , pool : transaction_pool. clone ( ) } ;
656+
657+ crate :: rpc:: create_full ( deps) . map_err ( Into :: into)
658+ } )
659+ } ;
660+
661+ let _rpc_handlers = sc_service:: spawn_tasks ( sc_service:: SpawnTasksParams {
662+ network,
663+ client,
664+ keystore : keystore_container. keystore ( ) ,
665+ task_manager : & mut task_manager,
666+ transaction_pool,
667+ rpc_builder,
668+ backend,
669+ system_rpc_tx,
670+ tx_handler_controller,
671+ sync_service,
672+ config,
673+ telemetry : telemetry. as_mut ( ) ,
674+ tracing_execute_block : None ,
675+ } ) ?;
676+
677+ Ok ( task_manager)
678+ }
0 commit comments