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