use std::{boxed::Box, sync::Arc, error::Error}; use sp_runtime::traits::{Block as BlockTrait}; use sp_inherents::CreateInherentDataProviders; use sp_consensus::DisableProofRecording; use sp_api::ProvideRuntimeApi; use sc_executor::{NativeVersion, NativeExecutionDispatch, NativeElseWasmExecutor}; use sc_transaction_pool::FullPool; use sc_network::NetworkService; use sc_service::{error::Error as ServiceError, Configuration, TaskManager, TFullClient}; use sc_telemetry::{Telemetry, TelemetryWorker}; pub(crate) use sc_tendermint::{ TendermintClientMinimal, TendermintValidator, TendermintImport, TendermintAuthority, TendermintSelectChain, import_queue, }; use serai_runtime::{self, MILLISECS_PER_BLOCK, opaque::Block, RuntimeApi}; type FullBackend = sc_service::TFullBackend; type FullSelectChain = TendermintSelectChain; type PartialComponents = sc_service::PartialComponents< FullClient, FullBackend, FullSelectChain, sc_consensus::DefaultImportQueue, sc_transaction_pool::FullPool, Option, >; pub struct ExecutorDispatch; impl NativeExecutionDispatch for ExecutorDispatch { #[cfg(feature = "runtime-benchmarks")] type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; #[cfg(not(feature = "runtime-benchmarks"))] type ExtendHostFunctions = (); fn dispatch(method: &str, data: &[u8]) -> Option> { serai_runtime::api::dispatch(method, data) } fn native_version() -> NativeVersion { serai_runtime::native_version() } } pub type FullClient = TFullClient>; pub struct Cidp; #[async_trait::async_trait] impl CreateInherentDataProviders for Cidp { type InherentDataProviders = (sp_timestamp::InherentDataProvider,); async fn create_inherent_data_providers( &self, _: ::Hash, _: (), ) -> Result> { Ok((sp_timestamp::InherentDataProvider::from_system_time(),)) } } pub struct TendermintValidatorFirm; impl TendermintClientMinimal for TendermintValidatorFirm { const BLOCK_TIME_IN_SECONDS: u32 = { (MILLISECS_PER_BLOCK / 1000) as u32 }; type Block = Block; type Backend = sc_client_db::Backend; type Api = >::Api; type Client = FullClient; } impl TendermintValidator for TendermintValidatorFirm { type CIDP = Cidp; type Environment = sc_basic_authorship::ProposerFactory< FullPool, Self::Backend, Self::Client, DisableProofRecording, >; type Network = Arc::Hash>>; } pub fn new_partial( config: &Configuration, ) -> Result<(TendermintImport, PartialComponents), ServiceError> { if config.keystore_remote.is_some() { return Err(ServiceError::Other("Remote Keystores are not supported".to_string())); } let telemetry = config .telemetry_endpoints .clone() .filter(|x| !x.is_empty()) .map(|endpoints| -> Result<_, sc_telemetry::Error> { let worker = TelemetryWorker::new(16)?; let telemetry = worker.handle().new_telemetry(endpoints); Ok((worker, telemetry)) }) .transpose()?; let executor = NativeElseWasmExecutor::::new( config.wasm_method, config.default_heap_pages, config.max_runtime_instances, config.runtime_cache_size, ); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, )?; let client = Arc::new(client); let telemetry = telemetry.map(|(worker, telemetry)| { task_manager.spawn_handle().spawn("telemetry", None, worker.run()); telemetry }); let transaction_pool = sc_transaction_pool::BasicPool::new_full( config.transaction_pool.clone(), config.role.is_authority().into(), config.prometheus_registry(), task_manager.spawn_essential_handle(), client.clone(), ); let (authority, import_queue) = import_queue( &task_manager.spawn_essential_handle(), client.clone(), config.prometheus_registry(), ); let select_chain = TendermintSelectChain::new(backend.clone()); Ok(( authority, sc_service::PartialComponents { client, backend, task_manager, import_queue, keystore_container, select_chain, transaction_pool, other: telemetry, }, )) } pub async fn new_full(config: Configuration) -> Result { let ( authority, sc_service::PartialComponents { client, backend, mut task_manager, import_queue, keystore_container, select_chain: _, other: mut telemetry, transaction_pool, }, ) = new_partial(&config)?; let (network, system_rpc_tx, tx_handler_controller, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), transaction_pool: transaction_pool.clone(), spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, warp_sync: None, })?; if config.offchain_worker.enabled { sc_service::build_offchain_workers( &config, task_manager.spawn_handle(), client.clone(), network.clone(), ); } let rpc_extensions_builder = { let client = client.clone(); let pool = transaction_pool.clone(); Box::new(move |deny_unsafe, _| { crate::rpc::create_full(crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe, }) .map_err(Into::into) }) }; let is_authority = config.role.is_authority(); let registry = config.prometheus_registry().cloned(); sc_service::spawn_tasks(sc_service::SpawnTasksParams { network: network.clone(), client: client.clone(), keystore: keystore_container.sync_keystore(), task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), rpc_builder: rpc_extensions_builder, backend, system_rpc_tx, tx_handler_controller, config, telemetry: telemetry.as_mut(), })?; if is_authority { task_manager.spawn_essential_handle().spawn( "tendermint", None, TendermintAuthority::new(authority).authority( (0, keystore_container.keystore()), Cidp, sc_basic_authorship::ProposerFactory::new( task_manager.spawn_handle(), client, transaction_pool, registry.as_ref(), telemetry.map(|telemtry| telemtry.handle()), ), network, None, ), ); } network_starter.start_network(); Ok(task_manager) }