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::store::{BlockRelevance, StoreError};
13#[cfg(feature = "testing")]
14use crate::test_utils::mock::MockRpcApi;
15use crate::{CachedPartialMmr, Client, ClientError};
16
17impl<AUTH> Client<AUTH> {
19 pub async fn get_block_header_by_num(
23 &self,
24 block_num: BlockNumber,
25 ) -> Result<Option<(BlockHeader, BlockRelevance)>, ClientError> {
26 self.store.get_block_header_by_num(block_num).await.map_err(Into::into)
27 }
28
29 pub async fn ensure_genesis_in_place(&mut self) -> Result<(), ClientError> {
33 if self.rpc_api.has_genesis_commitment().is_some() {
34 return Ok(());
35 }
36
37 let (genesis, _) = self
38 .rpc_api
39 .get_block_header_by_number(Some(BlockNumber::GENESIS), false)
40 .await?;
41
42 self.store.insert_block_header(&genesis, &[], false).await?;
45 self.rpc_api.set_genesis_commitment(genesis.commitment()).await?;
46 Ok(())
47 }
48
49 #[cfg(feature = "testing")]
55 pub async fn prepare_offline_bootstrap(&mut self) -> Result<(), ClientError> {
56 let limits = self.store.get_rpc_limits().await?.unwrap_or_default();
57 self.store.set_rpc_limits(limits).await?;
58 self.rpc_api.set_rpc_limits(limits).await;
59
60 if let Some((genesis, _)) = self.store.get_block_header_by_num(BlockNumber::GENESIS).await?
61 {
62 self.rpc_api.set_genesis_commitment(genesis.commitment()).await?;
63 return Ok(());
64 }
65
66 *self.test_rpc_api() = Arc::new(MockRpcApi::default());
67 self.ensure_genesis_in_place().await?;
68 Ok(())
69 }
70
71 pub async fn get_current_partial_mmr(&self) -> Result<PartialMmr, ClientError> {
74 if self.cache_partial_mmr_in_memory
75 && let Some(ref cached) = self.partial_mmr
76 && cached.store_peaks_hash == self.current_store_peaks_hash().await?
77 && cached.tracked_blocks_hash == self.current_tracked_blocks_hash().await?
78 {
79 return Ok(cached.mmr.clone());
80 }
81 self.store.get_current_partial_mmr().await.map_err(Into::into)
82 }
83
84 pub(crate) async fn cache_partial_mmr(&mut self, mmr: PartialMmr) -> Result<(), ClientError> {
88 if !self.cache_partial_mmr_in_memory {
89 return Ok(());
90 }
91
92 let store_peaks_hash = self.current_store_peaks_hash().await?;
93 let tracked_blocks_hash = self.current_tracked_blocks_hash().await?;
94 self.partial_mmr = Some(CachedPartialMmr {
95 store_peaks_hash,
96 tracked_blocks_hash,
97 mmr,
98 });
99 Ok(())
100 }
101
102 async fn current_store_peaks_hash(&self) -> Result<Word, ClientError> {
105 Ok(self.store.get_current_blockchain_peaks().await?.hash_peaks())
106 }
107
108 async fn current_tracked_blocks_hash(&self) -> Result<Word, ClientError> {
111 let tracked = self.store.get_tracked_block_header_numbers().await?;
113 let elements: Vec<Felt> = tracked
114 .iter()
115 .map(|&n| Felt::from(u32::try_from(n).expect("block number fits in u32")))
116 .collect();
117 Ok(Rpo256::hash_elements(&elements))
118 }
119
120 pub(crate) async fn get_and_store_authenticated_block(
129 &self,
130 block_num: BlockNumber,
131 current_partial_mmr: &mut PartialMmr,
132 ) -> Result<BlockHeader, ClientError> {
133 if current_partial_mmr.is_tracked(block_num.as_usize()) {
134 warn!("Current partial MMR already contains the requested data");
135 let (block_header, _) = self
136 .store
137 .get_block_header_by_num(block_num)
138 .await?
139 .expect("Block header should be tracked");
140 return Ok(block_header);
141 }
142
143 let (block_header, path_nodes) =
145 fetch_block_header(self.rpc_api.clone(), block_num, current_partial_mmr).await?;
146 let tracked_nodes = authenticated_block_nodes(&block_header, path_nodes);
147
148 self.store.insert_block_header(&block_header, &tracked_nodes, true).await?;
150
151 Ok(block_header)
152 }
153}
154
155pub(crate) fn adjust_merkle_path_for_forest(
167 merkle_path: &MerklePath,
168 block_num: BlockNumber,
169 forest: Forest,
170) -> Vec<(InOrderIndex, Word)> {
171 let expected_path_len = forest
172 .leaf_to_corresponding_tree(block_num.as_usize())
173 .expect("forest includes block number") as usize;
174
175 let mut idx = InOrderIndex::from_leaf_pos(block_num.as_usize());
176 let mut path_nodes = Vec::with_capacity(expected_path_len);
177
178 for node in merkle_path.nodes().iter().take(expected_path_len) {
179 path_nodes.push((idx.sibling(), *node));
180 idx = idx.parent();
181 }
182
183 path_nodes
184}
185
186pub(crate) fn track_block_in_mmr(
190 partial_mmr: &mut PartialMmr,
191 block_num: BlockNumber,
192 block_commitment: Word,
193 mmr_path: &MerklePath,
194) -> Result<Vec<(InOrderIndex, Word)>, ClientError> {
195 let path_nodes = adjust_merkle_path_for_forest(mmr_path, block_num, partial_mmr.forest());
196 let adjusted_path = MerklePath::new(path_nodes.iter().map(|(_, n)| *n).collect());
197
198 partial_mmr
199 .track(block_num.as_usize(), block_commitment, &adjusted_path)
200 .map_err(StoreError::MmrError)?;
201
202 Ok(path_nodes)
203}
204
205fn authenticated_block_nodes(
206 block_header: &BlockHeader,
207 mut path_nodes: Vec<(InOrderIndex, Word)>,
208) -> Vec<(InOrderIndex, Word)> {
209 let mut nodes = Vec::with_capacity(path_nodes.len() + 1);
210 nodes.push((
211 InOrderIndex::from_leaf_pos(block_header.block_num().as_usize()),
212 block_header.commitment(),
213 ));
214 nodes.append(&mut path_nodes);
215 nodes
216}
217
218pub(crate) async fn fetch_block_header(
219 rpc_api: Arc<dyn NodeRpcClient>,
220 block_num: BlockNumber,
221 current_partial_mmr: &mut PartialMmr,
222) -> Result<(BlockHeader, Vec<(InOrderIndex, Word)>), ClientError> {
223 let (block_header, mmr_proof) = rpc_api.get_block_header_with_proof(block_num).await?;
224
225 let path_nodes = track_block_in_mmr(
226 current_partial_mmr,
227 block_num,
228 block_header.commitment(),
229 mmr_proof.merkle_path(),
230 )?;
231
232 Ok((block_header, path_nodes))
233}
234
235#[cfg(test)]
236mod tests {
237 use miden_protocol::block::{BlockHeader, BlockNumber};
238 use miden_protocol::crypto::merkle::MerklePath;
239 use miden_protocol::crypto::merkle::mmr::{Forest, InOrderIndex, Mmr, PartialMmr};
240 use miden_protocol::transaction::TransactionKernel;
241 use miden_protocol::{Felt, Word};
242
243 use super::{adjust_merkle_path_for_forest, authenticated_block_nodes};
244
245 fn word(n: u64) -> Word {
246 Word::new([
247 Felt::new(n).expect("test value should fit into the base field"),
248 Felt::new(0).expect("zero is a valid field element"),
249 Felt::new(0).expect("zero is a valid field element"),
250 Felt::new(0).expect("zero is a valid field element"),
251 ])
252 }
253
254 #[test]
255 fn adjust_merkle_path_truncates_to_forest_bounds() {
256 let forest = Forest::new(5).expect("valid forest");
257 let block_num = BlockNumber::from(4u32);
259 let path = MerklePath::new(vec![word(1), word(2), word(3)]);
260
261 let adjusted = adjust_merkle_path_for_forest(&path, block_num, forest);
262 assert!(adjusted.is_empty());
264 }
265
266 #[test]
267 fn adjust_merkle_path_keeps_proof_valid_for_smaller_forest() {
268 let mut mmr = Mmr::new();
271 for value in 0u64..8 {
272 mmr.add(word(value)).expect("test MMR append should succeed");
273 }
274
275 let large_forest = Forest::new(8).expect("valid forest");
276 let small_forest = Forest::new(5).expect("valid forest");
277 let leaf_pos = 4usize;
278 let block_num = BlockNumber::from(u32::try_from(leaf_pos).unwrap());
279
280 let proof = mmr.open_at(leaf_pos, large_forest).expect("valid proof");
281 let adjusted_nodes =
282 adjust_merkle_path_for_forest(proof.merkle_path(), block_num, small_forest);
283 let adjusted_path = MerklePath::new(adjusted_nodes.iter().map(|(_, n)| *n).collect());
284
285 let peaks = mmr.peaks_at(small_forest).unwrap();
286 let mut partial = PartialMmr::from_peaks(peaks);
287 let leaf = mmr.get(leaf_pos).expect("leaf exists");
288
289 partial
290 .track(leaf_pos, leaf, &adjusted_path)
291 .expect("adjusted path should verify against smaller forest peaks");
292 }
293
294 #[test]
295 fn adjust_merkle_path_correct_indices() {
296 let forest = Forest::new(6).expect("valid forest");
298 let block_num = BlockNumber::from(1u32);
300 let nodes = vec![word(10), word(11), word(12), word(13)];
301 let path = MerklePath::new(nodes.clone());
302
303 let adjusted = adjust_merkle_path_for_forest(&path, block_num, forest);
304
305 assert_eq!(adjusted.len(), 2);
306 assert_eq!(adjusted[0].1, nodes[0]);
307 assert_eq!(adjusted[1].1, nodes[1]);
308
309 let mut idx = InOrderIndex::from_leaf_pos(1);
310 let expected0 = idx.sibling();
311 idx = idx.parent();
312 let expected1 = idx.sibling();
313
314 assert_eq!(adjusted[0].0, expected0);
315 assert_eq!(adjusted[1].0, expected1);
316 }
317
318 #[test]
319 fn adjust_path_limit_correct_when_siblings_in_bounds() {
320 let large_leaves = 8usize;
323 let small_leaves = 7usize;
324 let leaf_pos = 2usize;
325 let mut mmr = Mmr::new();
326 for value in 0u64..large_leaves as u64 {
327 mmr.add(word(value)).expect("test MMR append should succeed");
328 }
329
330 let small_forest = Forest::new(small_leaves).expect("valid forest");
331 let proof = mmr
332 .open_at(leaf_pos, Forest::new(large_leaves).expect("valid forest"))
333 .expect("valid proof");
334 let expected_depth =
335 small_forest.leaf_to_corresponding_tree(leaf_pos).expect("leaf is in forest") as usize;
336
337 let mut idx = InOrderIndex::from_leaf_pos(leaf_pos);
340 for _ in 0..expected_depth {
341 idx = idx.parent();
342 }
343 let next_sibling = idx.sibling();
344 let rightmost = InOrderIndex::from_leaf_pos(small_leaves - 1);
345 assert!(next_sibling <= rightmost);
346 assert!(proof.merkle_path().depth() as usize > expected_depth);
347
348 let adjusted = adjust_merkle_path_for_forest(
349 proof.merkle_path(),
350 BlockNumber::from(u32::try_from(leaf_pos).unwrap()),
351 small_forest,
352 );
353 assert_eq!(adjusted.len(), expected_depth);
354 }
355
356 #[test]
357 fn authenticated_block_nodes_include_leaf_commitment() {
358 let block_header = BlockHeader::mock(4, None, None, &[], TransactionKernel.to_commitment());
359 let path_nodes = vec![
360 (InOrderIndex::from_leaf_pos(4).sibling(), word(10)),
361 (InOrderIndex::from_leaf_pos(4).parent().sibling(), word(11)),
362 ];
363
364 let nodes = authenticated_block_nodes(&block_header, path_nodes.clone());
365
366 assert_eq!(nodes[0], (InOrderIndex::from_leaf_pos(4), block_header.commitment()));
367 assert_eq!(&nodes[1..], path_nodes.as_slice());
368 }
369}