Skip to main content

jam_tooling/
node_ext.rs

1use crate::{CodeInfo, Error};
2use codec::{Compact, Decode};
3use futures::StreamExt;
4use jam_program_blob_common::{ConventionalMetadata as Metadata, CrateInfo};
5use jam_std_common::{BlockDesc, Node, NodeError, NodeExt as _, Service, SyncStatus};
6use jam_types::{HeaderHash, RefineContext, ServiceId, Slot};
7use std::future::Future;
8
9pub trait NodeExt: Node {
10	fn query_service(
11		&self,
12		head: HeaderHash,
13		id: ServiceId,
14	) -> impl Future<Output = Result<(Service, CodeInfo<CrateInfo>), anyhow::Error>> + Send {
15		async move {
16			use CodeInfo::*;
17			let info = self.service_data(head, id).await?.ok_or(Error::ServiceNotFound)?;
18			let maybe_code = self.service_preimage(head, id, info.code_hash.0).await?;
19			let meta = match maybe_code {
20				None => CodeNotProvided(info.code_hash.0),
21				Some(code) =>
22					match <(Compact<u32>, Metadata)>::decode(&mut &code[..]).ok().map(|x| x.1) {
23						None => Undefined(info.code_hash.0),
24						Some(Metadata::Info(crate_info)) => Known(crate_info),
25					},
26			};
27			Ok((info, meta))
28		}
29	}
30
31	fn wait_for_sync(&self) -> impl Future<Output = Result<(), anyhow::Error>> + Send {
32		async move {
33			let mut sync_state = self.subscribe_sync_status().await?;
34			loop {
35				let status = sync_state.next().await;
36				match status {
37					Some(Ok(SyncStatus::Completed)) => break,
38					Some(Ok(_)) => continue,
39					Some(Err(e)) => return Err(e.into()),
40					None => break,
41				}
42			}
43			// Wait for any non-genesis block to be finalized
44			let mut final_block = self.subscribe_finalized_block().await?;
45			loop {
46				let status = final_block.next().await;
47				match status {
48					Some(Ok(block)) if block.slot > 0 => {
49						match self.parent(block.header_hash).await {
50							Ok(parent) => {
51								if parent.slot > 0 {
52									// Break only if the parent of the finalized block is
53									// non-genesis.
54									break;
55								}
56								eprintln!("Waiting for two non-genesis blocks to be finalized...");
57							},
58							Err(NodeError::BlockUnavailable(_)) => {
59								// Just wait for another block
60								eprintln!("Waiting for two non-genesis blocks to be finalized and available...");
61							},
62							Err(err) => return Err(err.into()),
63						}
64					},
65					Some(Ok(_)) => {
66						eprintln!("Waiting for two non-genesis blocks to be finalized...");
67					},
68					Some(Err(e)) => return Err(e.into()),
69					None => break,
70				}
71			}
72			Ok(())
73		}
74	}
75
76	/// Returns lookup anchor block description.
77	///
78	/// This is the parent of the finalized block.
79	fn lookup_anchor_block(&self) -> impl Future<Output = Result<BlockDesc, NodeError>> + Send {
80		async move {
81			let finalized = self.finalized_block().await?;
82			let parent = self.parent(finalized.header_hash).await?;
83			Ok(parent)
84		}
85	}
86
87	/// Create refine context from the current chain's best block.
88	///
89	/// - Best block's parent is used as an anchor.
90	/// - Finalized block is used as a lookup anchor.
91	///
92	/// Returns the resulting refine context and anchor slot.
93	fn create_refine_context(
94		&self,
95	) -> impl Future<Output = Result<(RefineContext, Slot), NodeError>> + Send {
96		async move {
97			// Use the parent of the latest finalized block as the lookup anchor. The parent is used
98			// as some nodes might have a bit of finality lag.
99			let BlockDesc { header_hash: lookup_anchor, slot: lookup_anchor_slot } =
100				self.lookup_anchor_block().await?;
101			// Use the parent of the best block as the anchor. The parent is used as some nodes
102			// might not have seen the best block yet.
103			let best_block = self.best_block().await?;
104			let parent = self.parent(best_block.header_hash).await?;
105			let anchor = parent.header_hash;
106			let state_root = self.state_root(anchor).await?;
107			let beefy_root = self.beefy_root(anchor).await?;
108			let context = RefineContext {
109				anchor,
110				state_root,
111				beefy_root,
112				lookup_anchor,
113				lookup_anchor_slot,
114				prerequisites: Default::default(),
115			};
116			Ok((context, parent.slot))
117		}
118	}
119}
120
121impl<T: Node + ?Sized> NodeExt for T {}