miden_client/sync/
block_header.rs

1use alloc::sync::Arc;
2use alloc::vec::Vec;
3
4use crypto::merkle::{InOrderIndex, MmrPeaks, PartialMmr};
5use miden_objects::Word;
6use miden_objects::block::{BlockHeader, BlockNumber};
7use miden_objects::crypto::merkle::{Forest, MerklePath};
8use miden_objects::crypto::{self};
9use tracing::warn;
10
11use crate::rpc::NodeRpcClient;
12use crate::store::StoreError;
13use crate::{Client, ClientError};
14
15/// Network information management methods.
16impl<AUTH> Client<AUTH> {
17    /// Attempts to retrieve the genesis block from the store. If not found,
18    /// it requests it from the node and store it.
19    pub async fn ensure_genesis_in_place(&mut self) -> Result<BlockHeader, ClientError> {
20        let genesis = match self.store.get_block_header_by_num(0.into()).await? {
21            Some((block, _)) => block,
22            None => self.retrieve_and_store_genesis().await?,
23        };
24
25        Ok(genesis)
26    }
27
28    /// Calls `get_block_header_by_number` requesting the genesis block and storing it
29    /// in the local database.
30    async fn retrieve_and_store_genesis(&mut self) -> Result<BlockHeader, ClientError> {
31        let (genesis_block, _) = self
32            .rpc_api
33            .get_block_header_by_number(Some(BlockNumber::GENESIS), false)
34            .await?;
35
36        let blank_mmr_peaks = MmrPeaks::new(Forest::empty(), vec![])
37            .expect("Blank MmrPeaks should not fail to instantiate");
38        self.store.insert_block_header(&genesis_block, blank_mmr_peaks, false).await?;
39        Ok(genesis_block)
40    }
41
42    /// Fetches from the store the current view of the chain's [`PartialMmr`].
43    pub async fn get_current_partial_mmr(&self) -> Result<PartialMmr, ClientError> {
44        self.store.get_current_partial_mmr().await.map_err(Into::into)
45    }
46
47    // HELPERS
48    // --------------------------------------------------------------------------------------------
49
50    /// Retrieves and stores a [`BlockHeader`] by number, and stores its authentication data as
51    /// well.
52    ///
53    /// If the store already contains MMR data for the requested block number, the request isn't
54    /// done and the stored block header is returned.
55    pub(crate) async fn get_and_store_authenticated_block(
56        &self,
57        block_num: BlockNumber,
58        current_partial_mmr: &mut PartialMmr,
59    ) -> Result<BlockHeader, ClientError> {
60        if current_partial_mmr.is_tracked(block_num.as_usize()) {
61            warn!("Current partial MMR already contains the requested data");
62            let (block_header, _) = self
63                .store
64                .get_block_header_by_num(block_num)
65                .await?
66                .expect("Block header should be tracked");
67            return Ok(block_header);
68        }
69
70        // Fetch the block header and MMR proof from the node
71        let (block_header, path_nodes) =
72            fetch_block_header(self.rpc_api.clone(), block_num, current_partial_mmr).await?;
73
74        // Insert header and MMR nodes
75        self.store
76            .insert_block_header(&block_header, current_partial_mmr.peaks(), true)
77            .await?;
78        self.store.insert_partial_blockchain_nodes(&path_nodes).await?;
79
80        Ok(block_header)
81    }
82}
83
84// UTILS
85// --------------------------------------------------------------------------------------------
86
87/// Returns a merkle path nodes for a specific block adjusted for a defined forest size.
88/// This function trims the merkle path to include only the nodes that are relevant for
89/// the MMR forest.
90///
91/// # Parameters
92/// - `merkle_path`: Original merkle path.
93/// - `block_num`: The block number for which the path is computed.
94/// - `forest`: The target size of the forest.
95pub(crate) fn adjust_merkle_path_for_forest(
96    merkle_path: &MerklePath,
97    block_num: BlockNumber,
98    forest: Forest,
99) -> Vec<(InOrderIndex, Word)> {
100    assert!(
101        forest.num_leaves() > block_num.as_usize(),
102        "Can't adjust merkle path for a forest that does not include the block number"
103    );
104
105    let rightmost_index = InOrderIndex::from_leaf_pos(forest.num_leaves() - 1);
106
107    let mut idx = InOrderIndex::from_leaf_pos(block_num.as_usize());
108    let mut path_nodes = vec![];
109    for node in merkle_path.iter() {
110        idx = idx.sibling();
111        // Rightmost index is always the biggest value, so if the path contains any node
112        // past it, we can discard it for our version of the forest
113        if idx <= rightmost_index {
114            path_nodes.push((idx, *node));
115        }
116        idx = idx.parent();
117    }
118
119    path_nodes
120}
121
122pub(crate) async fn fetch_block_header(
123    rpc_api: Arc<dyn NodeRpcClient>,
124    block_num: BlockNumber,
125    current_partial_mmr: &mut PartialMmr,
126) -> Result<(BlockHeader, Vec<(InOrderIndex, Word)>), ClientError> {
127    let (block_header, mmr_proof) = rpc_api.get_block_header_with_proof(block_num).await?;
128
129    // Trim merkle path to keep nodes relevant to our current PartialMmr since the node's MMR
130    // might be of a forest arbitrarily higher
131    let path_nodes = adjust_merkle_path_for_forest(
132        &mmr_proof.merkle_path,
133        block_num,
134        current_partial_mmr.forest(),
135    );
136
137    let merkle_path = MerklePath::new(path_nodes.iter().map(|(_, n)| *n).collect());
138
139    current_partial_mmr
140        .track(block_num.as_usize(), block_header.commitment(), &merkle_path)
141        .map_err(StoreError::MmrError)?;
142
143    Ok((block_header, path_nodes))
144}