1use alloc::sync::Arc;
2use alloc::vec::Vec;
3
4use miden_protocol::block::{BlockHeader, BlockNumber};
5use miden_protocol::crypto::hash::rpo::Rpo256;
6use miden_protocol::crypto::merkle::MerklePath;
7use miden_protocol::crypto::merkle::mmr::{Forest, InOrderIndex, PartialMmr};
8use miden_protocol::{Felt, Word};
9use tracing::warn;
10
11use crate::rpc::NodeRpcClient;
12use crate::rpc::domain::note::ResolvedSyncNotesBlock;
13use crate::store::{BlockRelevance, StoreError};
14#[cfg(feature = "testing")]
15use crate::test_utils::mock::MockRpcApi;
16use crate::{CachedPartialMmr, Client, ClientError};
17
18impl<AUTH> Client<AUTH> {
20 pub async fn get_block_header_by_num(
24 &self,
25 block_num: BlockNumber,
26 ) -> Result<Option<(BlockHeader, BlockRelevance)>, ClientError> {
27 self.store.get_block_header_by_num(block_num).await.map_err(Into::into)
28 }
29
30 pub async fn get_latest_block_header(&self) -> Result<BlockHeader, ClientError> {
32 let sync_height = self.store.get_sync_height().await?;
33 let Some((block_header, _)) = self.get_block_header_by_num(sync_height).await? else {
34 return Err(ClientError::DataStoreError(miden_tx::DataStoreError::BlockNotFound(
35 sync_height,
36 )));
37 };
38 Ok(block_header)
39 }
40
41 pub async fn ensure_genesis_in_place(&mut self) -> Result<(), ClientError> {
45 if self.rpc_api.has_genesis_commitment().is_some() {
46 return Ok(());
47 }
48
49 let (genesis, _) = self
50 .rpc_api
51 .get_block_header_by_number(Some(BlockNumber::GENESIS), false)
52 .await?;
53
54 self.store.insert_block_header(&genesis, &[], false).await?;
57 self.rpc_api.set_genesis_commitment(genesis.commitment()).await?;
58 Ok(())
59 }
60
61 #[cfg(feature = "testing")]
67 pub async fn prepare_offline_bootstrap(&mut self) -> Result<(), ClientError> {
68 let limits = self.store.get_rpc_limits().await?.unwrap_or_default();
69 self.store.set_rpc_limits(limits).await?;
70 self.rpc_api.set_rpc_limits(limits).await;
71
72 if let Some((genesis, _)) = self.store.get_block_header_by_num(BlockNumber::GENESIS).await?
73 {
74 self.rpc_api.set_genesis_commitment(genesis.commitment()).await?;
75 return Ok(());
76 }
77
78 let rpc = MockRpcApi::default();
79 self.add_protocol_config(rpc.protocol_config()).await?;
80 *self.test_rpc_api() = Arc::new(rpc);
81 self.ensure_genesis_in_place().await?;
82 Ok(())
83 }
84
85 pub async fn get_current_partial_mmr(&self) -> Result<PartialMmr, ClientError> {
88 if self.cache_partial_mmr_in_memory
89 && let Some(ref cached) = self.partial_mmr
90 && cached.store_peaks_hash == self.current_store_peaks_hash().await?
91 && cached.tracked_blocks_hash == self.current_tracked_blocks_hash().await?
92 {
93 return Ok(cached.mmr.clone());
94 }
95 self.store.get_current_partial_mmr().await.map_err(Into::into)
96 }
97
98 pub(crate) async fn cache_partial_mmr(&mut self, mmr: PartialMmr) -> Result<(), ClientError> {
102 if !self.cache_partial_mmr_in_memory {
103 return Ok(());
104 }
105
106 let store_peaks_hash = self.current_store_peaks_hash().await?;
107 let tracked_blocks_hash = self.current_tracked_blocks_hash().await?;
108 self.partial_mmr = Some(CachedPartialMmr {
109 store_peaks_hash,
110 tracked_blocks_hash,
111 mmr,
112 });
113 Ok(())
114 }
115
116 async fn current_store_peaks_hash(&self) -> Result<Word, ClientError> {
119 Ok(self.store.get_current_blockchain_peaks().await?.hash_peaks())
120 }
121
122 async fn current_tracked_blocks_hash(&self) -> Result<Word, ClientError> {
125 let tracked = self.store.get_tracked_block_header_numbers().await?;
127 let elements: Vec<Felt> = tracked
128 .iter()
129 .map(|&n| Felt::from(u32::try_from(n).expect("block number fits in u32")))
130 .collect();
131 Ok(Rpo256::hash_elements(&elements))
132 }
133
134 pub(crate) async fn insert_note_blocks(
137 &mut self,
138 blocks: &[ResolvedSyncNotesBlock],
139 partial_mmr: &mut PartialMmr,
140 ) -> Result<(), ClientError> {
141 let mut authenticated_blocks = Vec::with_capacity(blocks.len());
142 for block in blocks {
143 let block_num = block.block_header.block_num();
144 if partial_mmr.is_tracked(block_num.as_usize()) {
147 continue;
148 }
149
150 let path_nodes = track_block_in_mmr(
151 partial_mmr,
152 block_num,
153 block.block_header.commitment(),
154 &block.mmr_path,
155 )?;
156 authenticated_blocks.push((block.block_header.clone(), path_nodes));
157 }
158
159 for (block_header, path_nodes) in authenticated_blocks {
160 let nodes = authenticated_block_nodes(&block_header, path_nodes);
161 self.store.insert_block_header(&block_header, &nodes, true).await?;
162 }
163
164 Ok(())
165 }
166
167 pub(crate) async fn get_and_store_authenticated_block(
176 &self,
177 block_num: BlockNumber,
178 current_partial_mmr: &mut PartialMmr,
179 ) -> Result<BlockHeader, ClientError> {
180 if current_partial_mmr.is_tracked(block_num.as_usize()) {
181 warn!("Current partial MMR already contains the requested data");
182 let (block_header, _) = self
183 .store
184 .get_block_header_by_num(block_num)
185 .await?
186 .expect("Block header should be tracked");
187 return Ok(block_header);
188 }
189
190 let (block_header, path_nodes) =
192 fetch_block_header(self.rpc_api.clone(), block_num, current_partial_mmr).await?;
193 let tracked_nodes = authenticated_block_nodes(&block_header, path_nodes);
194
195 self.store.insert_block_header(&block_header, &tracked_nodes, true).await?;
198
199 Ok(block_header)
200 }
201}
202
203pub(crate) fn adjust_merkle_path_for_forest(
214 merkle_path: &MerklePath,
215 block_num: BlockNumber,
216 forest: Forest,
217) -> Vec<(InOrderIndex, Word)> {
218 let expected_path_len = forest
219 .leaf_to_corresponding_tree(block_num.as_usize())
220 .expect("forest includes block number") as usize;
221
222 let mut idx = InOrderIndex::from_leaf_pos(block_num.as_usize());
223 let mut path_nodes = Vec::with_capacity(expected_path_len);
224
225 for node in merkle_path.nodes().iter().take(expected_path_len) {
226 path_nodes.push((idx.sibling(), *node));
227 idx = idx.parent();
228 }
229
230 path_nodes
231}
232
233pub(crate) fn track_block_in_mmr(
236 partial_mmr: &mut PartialMmr,
237 block_num: BlockNumber,
238 block_commitment: Word,
239 mmr_path: &MerklePath,
240) -> Result<Vec<(InOrderIndex, Word)>, ClientError> {
241 let path_nodes = adjust_merkle_path_for_forest(mmr_path, block_num, partial_mmr.forest());
242 let adjusted_path = MerklePath::new(path_nodes.iter().map(|(_, n)| *n).collect());
243
244 partial_mmr
245 .track(block_num.as_usize(), block_commitment, &adjusted_path)
246 .map_err(StoreError::MmrError)?;
247
248 Ok(path_nodes)
249}
250
251fn authenticated_block_nodes(
252 block_header: &BlockHeader,
253 mut path_nodes: Vec<(InOrderIndex, Word)>,
254) -> Vec<(InOrderIndex, Word)> {
255 let mut nodes = Vec::with_capacity(path_nodes.len() + 1);
256 nodes.push((
257 InOrderIndex::from_leaf_pos(block_header.block_num().as_usize()),
258 block_header.commitment(),
259 ));
260 nodes.append(&mut path_nodes);
261 nodes
262}
263
264pub(crate) async fn fetch_block_header(
265 rpc_api: Arc<dyn NodeRpcClient>,
266 block_num: BlockNumber,
267 current_partial_mmr: &mut PartialMmr,
268) -> Result<(BlockHeader, Vec<(InOrderIndex, Word)>), ClientError> {
269 let (block_header, mmr_proof) = rpc_api.get_block_header_with_proof(block_num).await?;
270
271 let path_nodes = track_block_in_mmr(
272 current_partial_mmr,
273 block_num,
274 block_header.commitment(),
275 mmr_proof.merkle_path(),
276 )?;
277
278 Ok((block_header, path_nodes))
279}
280
281#[cfg(test)]
282mod tests {
283 use miden_protocol::block::{BlockHeader, BlockNumber};
284 use miden_protocol::crypto::merkle::MerklePath;
285 use miden_protocol::crypto::merkle::mmr::{Forest, InOrderIndex, Mmr, PartialMmr};
286 use miden_protocol::{Felt, Word};
287
288 use super::{adjust_merkle_path_for_forest, authenticated_block_nodes};
289
290 fn word(n: u64) -> Word {
291 Word::new([
292 Felt::new(n).expect("test value should fit into the base field"),
293 Felt::new(0).expect("zero is a valid field element"),
294 Felt::new(0).expect("zero is a valid field element"),
295 Felt::new(0).expect("zero is a valid field element"),
296 ])
297 }
298
299 #[test]
300 fn adjust_merkle_path_truncates_to_forest_bounds() {
301 let forest = Forest::new(5).expect("valid forest");
302 let block_num = BlockNumber::from(4u32);
304 let path = MerklePath::new(vec![word(1), word(2), word(3)]);
305
306 let adjusted = adjust_merkle_path_for_forest(&path, block_num, forest);
307 assert!(adjusted.is_empty());
309 }
310
311 #[test]
312 #[should_panic(expected = "forest includes block number")]
313 fn adjust_merkle_path_panics_for_block_beyond_forest() {
314 let forest = Forest::new(5).expect("valid forest");
318 let block_num = BlockNumber::from(5u32);
319 let path = MerklePath::new(vec![word(1), word(2), word(3)]);
320
321 adjust_merkle_path_for_forest(&path, block_num, forest);
322 }
323
324 #[test]
325 fn adjust_merkle_path_keeps_proof_valid_for_smaller_forest() {
326 let mut mmr = Mmr::new();
329 for value in 0u64..8 {
330 mmr.add(word(value)).expect("test MMR append should succeed");
331 }
332
333 let large_forest = Forest::new(8).expect("valid forest");
334 let small_forest = Forest::new(5).expect("valid forest");
335 let leaf_pos = 4usize;
336 let block_num = BlockNumber::from(u32::try_from(leaf_pos).unwrap());
337
338 let proof = mmr.open_at(leaf_pos, large_forest).expect("valid proof");
339 let adjusted_nodes =
340 adjust_merkle_path_for_forest(proof.merkle_path(), block_num, small_forest);
341 let adjusted_path = MerklePath::new(adjusted_nodes.iter().map(|(_, n)| *n).collect());
342
343 let peaks = mmr.peaks_at(small_forest).unwrap();
344 let mut partial = PartialMmr::from_peaks(peaks);
345 let leaf = mmr.get(leaf_pos).expect("leaf exists");
346
347 partial
348 .track(leaf_pos, leaf, &adjusted_path)
349 .expect("adjusted path should verify against smaller forest peaks");
350 }
351
352 #[test]
353 fn adjust_merkle_path_correct_indices() {
354 let forest = Forest::new(6).expect("valid forest");
356 let block_num = BlockNumber::from(1u32);
358 let nodes = vec![word(10), word(11), word(12), word(13)];
359 let path = MerklePath::new(nodes.clone());
360
361 let adjusted = adjust_merkle_path_for_forest(&path, block_num, forest);
362
363 assert_eq!(adjusted.len(), 2);
364 assert_eq!(adjusted[0].1, nodes[0]);
365 assert_eq!(adjusted[1].1, nodes[1]);
366
367 let mut idx = InOrderIndex::from_leaf_pos(1);
368 let expected0 = idx.sibling();
369 idx = idx.parent();
370 let expected1 = idx.sibling();
371
372 assert_eq!(adjusted[0].0, expected0);
373 assert_eq!(adjusted[1].0, expected1);
374 }
375
376 #[test]
377 fn adjust_path_limit_correct_when_siblings_in_bounds() {
378 let large_leaves = 8usize;
381 let small_leaves = 7usize;
382 let leaf_pos = 2usize;
383 let mut mmr = Mmr::new();
384 for value in 0u64..large_leaves as u64 {
385 mmr.add(word(value)).expect("test MMR append should succeed");
386 }
387
388 let small_forest = Forest::new(small_leaves).expect("valid forest");
389 let proof = mmr
390 .open_at(leaf_pos, Forest::new(large_leaves).expect("valid forest"))
391 .expect("valid proof");
392 let expected_depth =
393 small_forest.leaf_to_corresponding_tree(leaf_pos).expect("leaf is in forest") as usize;
394
395 let mut idx = InOrderIndex::from_leaf_pos(leaf_pos);
398 for _ in 0..expected_depth {
399 idx = idx.parent();
400 }
401 let next_sibling = idx.sibling();
402 let rightmost = InOrderIndex::from_leaf_pos(small_leaves - 1);
403 assert!(next_sibling <= rightmost);
404 assert!(proof.merkle_path().depth() as usize > expected_depth);
405
406 let adjusted = adjust_merkle_path_for_forest(
407 proof.merkle_path(),
408 BlockNumber::from(u32::try_from(leaf_pos).unwrap()),
409 small_forest,
410 );
411 assert_eq!(adjusted.len(), expected_depth);
412 }
413
414 #[test]
415 fn authenticated_block_nodes_include_leaf_commitment() {
416 let block_header = BlockHeader::mock(4, None, None, &[]);
417 let path_nodes = vec![
418 (InOrderIndex::from_leaf_pos(4).sibling(), word(10)),
419 (InOrderIndex::from_leaf_pos(4).parent().sibling(), word(11)),
420 ];
421
422 let nodes = authenticated_block_nodes(&block_header, path_nodes.clone());
423
424 assert_eq!(nodes[0], (InOrderIndex::from_leaf_pos(4), block_header.commitment()));
425 assert_eq!(&nodes[1..], path_nodes.as_slice());
426 }
427}