1use alloc::sync::Arc;
2use alloc::vec::Vec;
3
4use miden_protocol::Word;
5use miden_protocol::block::{BlockHeader, BlockNumber};
6use miden_protocol::crypto::merkle::MerklePath;
7use miden_protocol::crypto::merkle::mmr::{Forest, InOrderIndex, MmrPeaks, PartialMmr};
8#[cfg(feature = "testing")]
9use miden_protocol::transaction::TransactionKernel;
10use tracing::warn;
11
12use crate::rpc::NodeRpcClient;
13use crate::store::{BlockRelevance, StoreError};
14use crate::{Client, ClientError};
15
16impl<AUTH> Client<AUTH> {
18 pub async fn get_block_header_by_num(
22 &self,
23 block_num: BlockNumber,
24 ) -> Result<Option<(BlockHeader, BlockRelevance)>, ClientError> {
25 self.store.get_block_header_by_num(block_num).await.map_err(Into::into)
26 }
27
28 pub async fn ensure_genesis_in_place(&mut self) -> Result<(), ClientError> {
32 if self.rpc_api.has_genesis_commitment().is_some() {
33 return Ok(());
34 }
35
36 let (genesis, _) = self
37 .rpc_api
38 .get_block_header_by_number(Some(BlockNumber::GENESIS), false)
39 .await?;
40
41 let blank_mmr_peaks = MmrPeaks::new(Forest::empty(), vec![])
42 .expect("Blank MmrPeaks should not fail to instantiate");
43 self.store.insert_block_header(&genesis, blank_mmr_peaks, false).await?;
44 self.rpc_api.set_genesis_commitment(genesis.commitment()).await?;
45 Ok(())
46 }
47
48 #[cfg(feature = "testing")]
54 pub async fn prepare_offline_bootstrap(&mut self) -> Result<(), ClientError> {
55 let limits = self.store.get_rpc_limits().await?.unwrap_or_default();
56 self.store.set_rpc_limits(limits).await?;
57 self.rpc_api.set_rpc_limits(limits).await;
58
59 if let Some((genesis, _)) = self.store.get_block_header_by_num(BlockNumber::GENESIS).await?
60 {
61 self.rpc_api.set_genesis_commitment(genesis.commitment()).await?;
62 return Ok(());
63 }
64
65 let genesis = synthetic_offline_genesis_header();
66 let blank_mmr_peaks = MmrPeaks::new(Forest::empty(), vec![])
67 .expect("Blank MmrPeaks should not fail to instantiate");
68 self.store.insert_block_header(&genesis, blank_mmr_peaks, false).await?;
69 self.rpc_api.set_genesis_commitment(genesis.commitment()).await?;
70 Ok(())
71 }
72
73 pub async fn get_current_partial_mmr(&self) -> Result<PartialMmr, ClientError> {
75 self.store.get_current_partial_mmr().await.map_err(Into::into)
76 }
77
78 pub(crate) async fn get_and_store_authenticated_block(
87 &self,
88 block_num: BlockNumber,
89 current_partial_mmr: &mut PartialMmr,
90 ) -> Result<BlockHeader, ClientError> {
91 if current_partial_mmr.is_tracked(block_num.as_usize()) {
92 warn!("Current partial MMR already contains the requested data");
93 let (block_header, _) = self
94 .store
95 .get_block_header_by_num(block_num)
96 .await?
97 .expect("Block header should be tracked");
98 return Ok(block_header);
99 }
100
101 let (block_header, path_nodes) =
103 fetch_block_header(self.rpc_api.clone(), block_num, current_partial_mmr).await?;
104 let tracked_nodes = authenticated_block_nodes(&block_header, path_nodes);
105
106 self.store
108 .insert_block_header(&block_header, current_partial_mmr.peaks(), true)
109 .await?;
110 self.store.insert_partial_blockchain_nodes(&tracked_nodes).await?;
111
112 Ok(block_header)
113 }
114}
115
116#[cfg(feature = "testing")]
117fn synthetic_offline_genesis_header() -> BlockHeader {
118 BlockHeader::mock(BlockNumber::GENESIS, None, None, &[], TransactionKernel.to_commitment())
119}
120
121pub(crate) fn adjust_merkle_path_for_forest(
133 merkle_path: &MerklePath,
134 block_num: BlockNumber,
135 forest: Forest,
136) -> Vec<(InOrderIndex, Word)> {
137 let expected_path_len = forest
138 .leaf_to_corresponding_tree(block_num.as_usize())
139 .expect("forest includes block number") as usize;
140
141 let mut idx = InOrderIndex::from_leaf_pos(block_num.as_usize());
142 let mut path_nodes = Vec::with_capacity(expected_path_len);
143
144 for node in merkle_path.nodes().iter().take(expected_path_len) {
145 path_nodes.push((idx.sibling(), *node));
146 idx = idx.parent();
147 }
148
149 path_nodes
150}
151
152fn authenticated_block_nodes(
153 block_header: &BlockHeader,
154 mut path_nodes: Vec<(InOrderIndex, Word)>,
155) -> Vec<(InOrderIndex, Word)> {
156 let mut nodes = Vec::with_capacity(path_nodes.len() + 1);
157 nodes.push((
158 InOrderIndex::from_leaf_pos(block_header.block_num().as_usize()),
159 block_header.commitment(),
160 ));
161 nodes.append(&mut path_nodes);
162 nodes
163}
164
165pub(crate) async fn fetch_block_header(
166 rpc_api: Arc<dyn NodeRpcClient>,
167 block_num: BlockNumber,
168 current_partial_mmr: &mut PartialMmr,
169) -> Result<(BlockHeader, Vec<(InOrderIndex, Word)>), ClientError> {
170 let (block_header, mmr_proof) = rpc_api.get_block_header_with_proof(block_num).await?;
171
172 let path_nodes = adjust_merkle_path_for_forest(
175 mmr_proof.merkle_path(),
176 block_num,
177 current_partial_mmr.forest(),
178 );
179
180 let merkle_path = MerklePath::new(path_nodes.iter().map(|(_, n)| *n).collect());
181
182 current_partial_mmr
183 .track(block_num.as_usize(), block_header.commitment(), &merkle_path)
184 .map_err(StoreError::MmrError)?;
185
186 Ok((block_header, path_nodes))
187}
188
189#[cfg(test)]
190mod tests {
191 use miden_protocol::block::account_tree::AccountTree;
192 use miden_protocol::block::{BlockHeader, BlockNumber};
193 use miden_protocol::crypto::merkle::MerklePath;
194 use miden_protocol::crypto::merkle::mmr::{Forest, InOrderIndex, Mmr, PartialMmr};
195 use miden_protocol::crypto::merkle::smt::Smt;
196 use miden_protocol::transaction::TransactionKernel;
197 use miden_protocol::{Felt, Word};
198
199 #[cfg(feature = "testing")]
200 use super::synthetic_offline_genesis_header;
201 use super::{adjust_merkle_path_for_forest, authenticated_block_nodes};
202
203 fn word(n: u64) -> Word {
204 Word::new([Felt::new(n), Felt::new(0), Felt::new(0), Felt::new(0)])
205 }
206
207 #[test]
208 fn adjust_merkle_path_truncates_to_forest_bounds() {
209 let forest = Forest::new(5);
210 let block_num = BlockNumber::from(4u32);
212 let path = MerklePath::new(vec![word(1), word(2), word(3)]);
213
214 let adjusted = adjust_merkle_path_for_forest(&path, block_num, forest);
215 assert!(adjusted.is_empty());
217 }
218
219 #[test]
220 fn adjust_merkle_path_keeps_proof_valid_for_smaller_forest() {
221 let mut mmr = Mmr::new();
224 for value in 0u64..8 {
225 mmr.add(word(value));
226 }
227
228 let large_forest = Forest::new(8);
229 let small_forest = Forest::new(5);
230 let leaf_pos = 4usize;
231 let block_num = BlockNumber::from(u32::try_from(leaf_pos).unwrap());
232
233 let proof = mmr.open_at(leaf_pos, large_forest).expect("valid proof");
234 let adjusted_nodes =
235 adjust_merkle_path_for_forest(proof.merkle_path(), block_num, small_forest);
236 let adjusted_path = MerklePath::new(adjusted_nodes.iter().map(|(_, n)| *n).collect());
237
238 let peaks = mmr.peaks_at(small_forest).unwrap();
239 let mut partial = PartialMmr::from_peaks(peaks);
240 let leaf = mmr.get(leaf_pos).expect("leaf exists");
241
242 partial
243 .track(leaf_pos, leaf, &adjusted_path)
244 .expect("adjusted path should verify against smaller forest peaks");
245 }
246
247 #[test]
248 fn adjust_merkle_path_correct_indices() {
249 let forest = Forest::new(6);
251 let block_num = BlockNumber::from(1u32);
253 let nodes = vec![word(10), word(11), word(12), word(13)];
254 let path = MerklePath::new(nodes.clone());
255
256 let adjusted = adjust_merkle_path_for_forest(&path, block_num, forest);
257
258 assert_eq!(adjusted.len(), 2);
259 assert_eq!(adjusted[0].1, nodes[0]);
260 assert_eq!(adjusted[1].1, nodes[1]);
261
262 let mut idx = InOrderIndex::from_leaf_pos(1);
263 let expected0 = idx.sibling();
264 idx = idx.parent();
265 let expected1 = idx.sibling();
266
267 assert_eq!(adjusted[0].0, expected0);
268 assert_eq!(adjusted[1].0, expected1);
269 }
270
271 #[test]
272 fn adjust_path_limit_correct_when_siblings_in_bounds() {
273 let large_leaves = 8usize;
276 let small_leaves = 7usize;
277 let leaf_pos = 2usize;
278 let mut mmr = Mmr::new();
279 for value in 0u64..large_leaves as u64 {
280 mmr.add(word(value));
281 }
282
283 let small_forest = Forest::new(small_leaves);
284 let proof = mmr.open_at(leaf_pos, Forest::new(large_leaves)).expect("valid proof");
285 let expected_depth =
286 small_forest.leaf_to_corresponding_tree(leaf_pos).expect("leaf is in forest") as usize;
287
288 let mut idx = InOrderIndex::from_leaf_pos(leaf_pos);
291 for _ in 0..expected_depth {
292 idx = idx.parent();
293 }
294 let next_sibling = idx.sibling();
295 let rightmost = InOrderIndex::from_leaf_pos(small_leaves - 1);
296 assert!(next_sibling <= rightmost);
297 assert!(proof.merkle_path().depth() as usize > expected_depth);
298
299 let adjusted = adjust_merkle_path_for_forest(
300 proof.merkle_path(),
301 BlockNumber::from(u32::try_from(leaf_pos).unwrap()),
302 small_forest,
303 );
304 assert_eq!(adjusted.len(), expected_depth);
305 }
306
307 #[test]
308 fn authenticated_block_nodes_include_leaf_commitment() {
309 let block_header = BlockHeader::mock(4, None, None, &[], TransactionKernel.to_commitment());
310 let path_nodes = vec![
311 (InOrderIndex::from_leaf_pos(4).sibling(), word(10)),
312 (InOrderIndex::from_leaf_pos(4).parent().sibling(), word(11)),
313 ];
314
315 let nodes = authenticated_block_nodes(&block_header, path_nodes.clone());
316
317 assert_eq!(nodes[0], (InOrderIndex::from_leaf_pos(4), block_header.commitment()));
318 assert_eq!(&nodes[1..], path_nodes.as_slice());
319 }
320
321 #[test]
322 #[cfg(feature = "testing")]
323 fn synthetic_offline_genesis_header_uses_mock_genesis() {
324 let genesis = synthetic_offline_genesis_header();
325
326 assert_eq!(genesis.block_num(), BlockNumber::GENESIS);
327 assert_eq!(genesis.account_root(), AccountTree::<Smt>::default().root());
328 assert_eq!(genesis.tx_kernel_commitment(), TransactionKernel.to_commitment());
329 }
330}