1#![allow(deprecated)]
4#![allow(clippy::result_large_err)]
5
6pub mod chain_spec;
7mod finalizer;
8mod genesis_block_builder;
9pub mod network_request_handler;
10mod transaction_adapter;
11pub mod transaction_pool;
12
13use bitcoin::hashes::Hash;
14use futures::FutureExt;
15use sc_client_api::{AuxStore, HeaderBackend};
16use sc_consensus::import_queue::BasicQueue;
17use sc_consensus_nakamoto::SubstrateImportQueueVerifier;
18use sc_executor::NativeElseWasmExecutor;
19use sc_network_sync::SyncingService;
20use sc_service::config::PrometheusConfig;
21use sc_service::error::Error as ServiceError;
22use sc_service::{
23 Configuration, KeystoreContainer, MetricsService, NativeExecutionDispatch, TaskManager,
24};
25use sc_telemetry::{Telemetry, TelemetryWorker};
26use sc_utils::mpsc::TracingUnboundedSender;
27use sp_core::Encode;
28use sp_runtime::traits::Block as BlockT;
29use std::ops::Deref;
30use std::path::Path;
31use std::sync::Arc;
32use subcoin_runtime::RuntimeApi;
33use subcoin_runtime::interface::OpaqueBlock as Block;
34
35pub use finalizer::SubcoinFinalizer;
36pub use genesis_block_builder::GenesisBlockBuilder;
37pub use transaction_adapter::TransactionAdapter;
38
39pub type ChainSpec = sc_service::GenericChainSpec;
41
42pub type FullClient =
44 sc_service::TFullClient<Block, RuntimeApi, NativeElseWasmExecutor<SubcoinExecutorDispatch>>;
45pub type FullBackend = sc_service::TFullBackend<Block>;
46type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
47
48pub struct SubcoinExecutorDispatch;
50
51impl NativeExecutionDispatch for SubcoinExecutorDispatch {
52 type ExtendHostFunctions = ();
53
54 fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> {
55 subcoin_runtime::api::dispatch(method, data)
56 }
57
58 fn native_version() -> sc_executor::NativeVersion {
59 subcoin_runtime::native_version()
60 }
61}
62
63pub struct CoinStorageKey;
65
66impl subcoin_primitives::CoinStorageKey for CoinStorageKey {
67 fn storage_key(&self, txid: bitcoin::Txid, vout: u32) -> Vec<u8> {
68 pallet_bitcoin::coin_storage_key::<subcoin_runtime::Runtime>(txid, vout)
69 }
70
71 fn storage_prefix(&self) -> [u8; 32] {
72 pallet_bitcoin::coin_storage_prefix::<subcoin_runtime::Runtime>()
73 }
74}
75
76pub struct NodeComponents {
78 pub client: Arc<FullClient>,
80 pub backend: Arc<FullBackend>,
82 pub executor: NativeElseWasmExecutor<SubcoinExecutorDispatch>,
84 pub task_manager: TaskManager,
86 pub keystore_container: KeystoreContainer,
87 pub telemetry: Option<Telemetry>,
88}
89
90pub struct SubcoinConfiguration<'a> {
92 pub network: bitcoin::Network,
93 pub config: &'a Configuration,
94 pub no_hardware_benchmarks: bool,
95 pub storage_monitor: sc_storage_monitor::StorageMonitorParams,
96}
97
98impl<'a> SubcoinConfiguration<'a> {
99 pub fn test_config(config: &'a Configuration) -> Self {
101 Self {
102 network: bitcoin::Network::Bitcoin,
103 config,
104 no_hardware_benchmarks: true,
105 storage_monitor: Default::default(),
106 }
107 }
108}
109
110impl Deref for SubcoinConfiguration<'_> {
111 type Target = Configuration;
112
113 fn deref(&self) -> &Self::Target {
114 self.config
115 }
116}
117
118pub fn initialize_genesis_block_hash_mapping<
120 Block: BlockT,
121 Client: HeaderBackend<Block> + AuxStore,
122>(
123 client: &Client,
124 bitcoin_network: bitcoin::Network,
125) {
126 let substrate_genesis_hash: <Block as BlockT>::Hash = client.info().genesis_hash;
128 let bitcoin_genesis_hash = bitcoin::constants::genesis_block(bitcoin_network).block_hash();
129 client
130 .insert_aux(
131 &[(
132 bitcoin_genesis_hash.to_byte_array().as_slice(),
133 substrate_genesis_hash.encode().as_slice(),
134 )],
135 [],
136 )
137 .expect("Failed to store genesis block hash mapping");
138}
139
140pub fn new_node(config: SubcoinConfiguration) -> Result<NodeComponents, ServiceError> {
142 let SubcoinConfiguration {
143 network: bitcoin_network,
144 config,
145 no_hardware_benchmarks: _,
146 storage_monitor,
147 } = config;
148
149 let telemetry = config
150 .telemetry_endpoints
151 .clone()
152 .filter(|x| !x.is_empty())
153 .map(|endpoints| -> Result<_, sc_telemetry::Error> {
154 let worker = TelemetryWorker::new(16)?;
155 let telemetry = worker.handle().new_telemetry(endpoints);
156 Ok((worker, telemetry))
157 })
158 .transpose()?;
159
160 let executor = sc_service::new_native_or_wasm_executor(config);
162
163 let backend = sc_service::new_db_backend(config.db_config())?;
164
165 let genesis_block_builder = GenesisBlockBuilder::<_, _, _, TransactionAdapter>::new(
166 bitcoin_network,
167 config.chain_spec.as_storage_builder(),
168 !config.no_genesis(),
169 backend.clone(),
170 executor.clone(),
171 )?;
172
173 let (client, backend, keystore_container, task_manager) =
174 sc_service::new_full_parts_with_genesis_builder::<Block, RuntimeApi, _, _>(
175 config,
176 telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
177 executor.clone(),
178 backend,
179 genesis_block_builder,
180 false,
181 )?;
182
183 initialize_genesis_block_hash_mapping(&client, bitcoin_network);
185
186 let client = Arc::new(client);
187
188 let telemetry = telemetry.map(|(worker, telemetry)| {
189 task_manager
190 .spawn_handle()
191 .spawn("telemetry", None, worker.run());
192 telemetry
193 });
194
195 let database_path = config.database.path().map(Path::to_path_buf);
196
197 if let Some(database_path) = database_path {
228 sc_storage_monitor::StorageMonitorService::try_spawn(
229 storage_monitor,
230 database_path,
231 &task_manager.spawn_essential_handle(),
232 )
233 .map_err(|e| ServiceError::Application(e.into()))?;
234 }
235
236 Ok(NodeComponents {
237 client,
238 backend,
239 executor,
240 task_manager,
241 keystore_container,
242 telemetry,
243 })
244}
245
246type SubstrateNetworkingParts = (
247 TracingUnboundedSender<sc_rpc::system::Request<Block>>,
248 Arc<SyncingService<Block>>,
249);
250
251pub fn start_substrate_network<N>(
253 config: &mut Configuration,
254 client: Arc<FullClient>,
255 _backend: Arc<FullBackend>,
256 task_manager: &mut TaskManager,
257 bitcoin_network: bitcoin::Network,
258 mut telemetry: Option<Telemetry>,
259) -> Result<SubstrateNetworkingParts, ServiceError>
260where
261 N: sc_network::NetworkBackend<Block, <Block as BlockT>::Hash>,
262{
263 let mut net_config = sc_network::config::FullNetworkConfiguration::<
264 Block,
265 <Block as BlockT>::Hash,
266 N,
267 >::new(&config.network, config.prometheus_registry().cloned());
268
269 let network_request_protocol_config = {
270 let (handler, protocol_config) = network_request_handler::NetworkRequestHandler::new::<N>(
272 &config.protocol_id(),
273 config.chain_spec.fork_id(),
274 client.clone(),
275 100,
276 );
277 task_manager.spawn_handle().spawn(
278 "subcoin-network-request-handler",
279 Some("networking"),
280 handler.run(),
281 );
282 protocol_config
283 };
284
285 net_config.add_request_response_protocol(network_request_protocol_config);
286
287 let metrics = N::register_notification_metrics(config.prometheus_registry());
288
289 let transaction_pool = Arc::from(
290 sc_transaction_pool::Builder::new(
291 task_manager.spawn_essential_handle(),
292 client.clone(),
293 config.role.is_authority().into(),
294 )
295 .with_options(config.transaction_pool.clone())
296 .with_prometheus(config.prometheus_registry())
297 .build(),
298 );
299
300 let import_queue = BasicQueue::new(
301 SubstrateImportQueueVerifier::new(client.clone(), bitcoin_network),
302 Box::new(client.clone()),
303 None,
304 &task_manager.spawn_essential_handle(),
305 None,
306 );
307
308 let (network, system_rpc_tx, _tx_handler_controller, sync_service) =
309 sc_service::build_network(sc_service::BuildNetworkParams {
310 config,
311 net_config,
312 client: client.clone(),
313 transaction_pool: transaction_pool.clone(),
314 spawn_handle: task_manager.spawn_handle(),
315 import_queue,
316 block_announce_validator_builder: None,
317 warp_sync_config: None,
318 block_relay: None,
319 metrics,
320 })?;
321
322 let spawn_handle = task_manager.spawn_handle();
324
325 let sysinfo = sc_sysinfo::gather_sysinfo();
326 sc_sysinfo::print_sysinfo(&sysinfo);
327
328 let telemetry = telemetry
329 .as_mut()
330 .map(|telemetry| {
331 sc_service::init_telemetry(
332 config.network.node_name.clone(),
333 config.impl_name.clone(),
334 config.impl_version.clone(),
335 config.chain_spec.name().to_string(),
336 config.role.is_authority(),
337 network.clone(),
338 client.clone(),
339 telemetry,
340 Some(sysinfo),
341 )
342 })
343 .transpose()?;
344
345 let metrics_service =
347 if let Some(PrometheusConfig { port, registry }) = config.prometheus_config.clone() {
348 let metrics = MetricsService::with_prometheus(
350 telemetry,
351 ®istry,
352 config.role,
353 &config.network.node_name,
354 &config.impl_version,
355 )?;
356 spawn_handle.spawn(
357 "prometheus-endpoint",
358 None,
359 substrate_prometheus_endpoint::init_prometheus(port, registry).map(drop),
360 );
361
362 metrics
363 } else {
364 MetricsService::new(telemetry)
365 };
366
367 spawn_handle.spawn(
369 "telemetry-periodic-send",
370 None,
371 metrics_service.run(
372 client.clone(),
373 transaction_pool.clone(),
374 network.clone(),
375 sync_service.clone(),
376 ),
377 );
378
379 spawn_handle.spawn(
380 "substrate-informant",
381 None,
382 sc_informant::build(client.clone(), Arc::new(network), sync_service.clone()),
383 );
384
385 Ok((system_rpc_tx, sync_service))
386}
387
388type PartialComponents = sc_service::PartialComponents<
389 FullClient,
390 FullBackend,
391 FullSelectChain,
392 sc_consensus::DefaultImportQueue<Block>,
393 sc_transaction_pool::TransactionPoolHandle<Block, FullClient>,
394 Option<Telemetry>,
395>;
396
397pub fn new_partial(
399 config: &Configuration,
400 bitcoin_network: bitcoin::Network,
401) -> Result<PartialComponents, ServiceError> {
402 let telemetry = config
403 .telemetry_endpoints
404 .clone()
405 .filter(|x| !x.is_empty())
406 .map(|endpoints| -> Result<_, sc_telemetry::Error> {
407 let worker = TelemetryWorker::new(16)?;
408 let telemetry = worker.handle().new_telemetry(endpoints);
409 Ok((worker, telemetry))
410 })
411 .transpose()?;
412
413 let executor = sc_service::new_native_or_wasm_executor(config);
414
415 let (client, backend, keystore_container, task_manager) =
416 sc_service::new_full_parts::<Block, RuntimeApi, _>(
417 config,
418 telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
419 executor,
420 )?;
421 let client = Arc::new(client);
422
423 let telemetry = telemetry.map(|(worker, telemetry)| {
424 task_manager
425 .spawn_handle()
426 .spawn("telemetry", None, worker.run());
427 telemetry
428 });
429
430 let select_chain = sc_consensus::LongestChain::new(backend.clone());
431
432 let transaction_pool = Arc::from(
433 sc_transaction_pool::Builder::new(
434 task_manager.spawn_essential_handle(),
435 client.clone(),
436 config.role.is_authority().into(),
437 )
438 .with_options(config.transaction_pool.clone())
439 .with_prometheus(config.prometheus_registry())
440 .build(),
441 );
442
443 let import_queue = BasicQueue::new(
444 SubstrateImportQueueVerifier::new(client.clone(), bitcoin_network),
445 Box::new(client.clone()),
446 None,
447 &task_manager.spawn_essential_handle(),
448 None,
449 );
450
451 Ok(sc_service::PartialComponents {
452 client,
453 backend,
454 task_manager,
455 import_queue,
456 keystore_container,
457 select_chain,
458 transaction_pool,
459 other: (telemetry),
460 })
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466 use crate::{NodeComponents, SubcoinConfiguration, new_node};
467 use subcoin_primitives::extract_bitcoin_block_hash;
468 use tokio::runtime::Handle;
469
470 #[tokio::test]
471 async fn test_bitcoin_block_hash_in_substrate_header() {
472 let runtime_handle = Handle::current();
473 let config = subcoin_test_service::test_configuration(runtime_handle);
474 let NodeComponents { client, .. } =
475 new_node(SubcoinConfiguration::test_config(&config)).expect("Failed to create node");
476
477 let substrate_genesis_header = client.header(client.info().genesis_hash).unwrap().unwrap();
478 let bitcoin_genesis_header =
479 bitcoin::constants::genesis_block(bitcoin::Network::Bitcoin).header;
480
481 let bitcoin_genesis_block_hash = bitcoin_genesis_header.block_hash();
482
483 assert_eq!(
484 extract_bitcoin_block_hash::<subcoin_runtime::interface::OpaqueBlock>(
485 &substrate_genesis_header
486 )
487 .unwrap(),
488 bitcoin_genesis_block_hash,
489 );
490 }
491}