domain_client_operator/
fetch_domain_bootstrap_info.rs1use futures::StreamExt;
2use sc_client_api::backend::Backend;
3use sc_client_api::{BlockchainEvents, ImportNotifications};
4use sp_api::ProvideRuntimeApi;
5use sp_blockchain::HeaderBackend;
6use sp_domains::{DomainId, DomainInstanceData, DomainsApi, DomainsDigestItem};
7use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
8
9#[derive(Debug)]
10pub struct BootstrapResult<CBlock: BlockT> {
11 pub domain_instance_data: DomainInstanceData,
14 pub domain_created_at: NumberFor<CBlock>,
16 pub imported_block_notification_stream: ImportNotifications<CBlock>,
22}
23
24pub async fn fetch_domain_bootstrap_info<Block, CBlock, CClient, DomainBackend>(
25 consensus_client: &CClient,
26 domain_backend: &DomainBackend,
27 self_domain_id: DomainId,
28) -> Result<BootstrapResult<CBlock>, Box<dyn std::error::Error>>
29where
30 Block: BlockT,
31 CBlock: BlockT,
32 CClient: HeaderBackend<CBlock> + ProvideRuntimeApi<CBlock> + BlockchainEvents<CBlock>,
33 CClient::Api: DomainsApi<CBlock, Block::Header>,
34 DomainBackend: Backend<Block>,
35{
36 let is_domain_started = domain_backend.blockchain().info().finalized_state.is_some();
39
40 let mut imported_block_notification_stream =
41 consensus_client.every_import_notification_stream();
42
43 let best_hash = consensus_client.info().best_hash;
45 if let Some((domain_instance_data, domain_created_at)) = consensus_client
46 .runtime_api()
47 .domain_instance_data(best_hash, self_domain_id)?
48 {
49 let domain_best_number = consensus_client
50 .runtime_api()
51 .domain_best_number(best_hash, self_domain_id)?
52 .unwrap_or_default();
53
54 if !is_domain_started && !domain_best_number.is_zero() {
65 return Err(
66 "An existing consensus node can't be restarted as a domain node, in order to
67 proceed please wipe the `db` and `domains` folders"
68 .to_string()
69 .into(),
70 );
71 }
72
73 return Ok(BootstrapResult {
74 domain_instance_data,
75 domain_created_at,
76 imported_block_notification_stream,
77 });
78 }
79
80 if is_domain_started {
84 return Err(
85 "The domain chain is ahead of the consensus chain, inconsistent `db` and `domains`
86 folders from the last run"
87 .to_string()
88 .into(),
89 );
90 }
91
92 let (domain_instance_data, domain_created_at) = 'outer: loop {
94 if let Some(block_imported) = imported_block_notification_stream.next().await {
95 let header = block_imported.header;
96 for item in header.digest().logs.iter() {
97 if let Some(domain_id) = item.as_domain_instantiation() {
98 if domain_id == self_domain_id {
99 let res = consensus_client
100 .runtime_api()
101 .domain_instance_data(header.hash(), domain_id)?;
102
103 match res {
104 Some(data) => break 'outer data,
105 None => {
106 return Err(format!(
107 "Failed to get domain instance data for domain {domain_id:?}"
108 )
109 .into())
110 }
111 }
112 }
113 }
114 }
115 } else {
116 return Err("Imported block notification stream end unexpectedly"
117 .to_string()
118 .into());
119 }
120 };
121
122 Ok(BootstrapResult {
123 domain_instance_data,
124 domain_created_at,
125 imported_block_notification_stream,
126 })
127}