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