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};
8use tracing::warn;
9
10use crate::rpc::NodeRpcClient;
11use crate::store::{BlockRelevance, StoreError};
12use crate::{Client, ClientError};
13
14impl<AUTH> Client<AUTH> {
16 pub async fn get_block_header_by_num(
20 &self,
21 block_num: BlockNumber,
22 ) -> Result<Option<(BlockHeader, BlockRelevance)>, ClientError> {
23 self.store.get_block_header_by_num(block_num).await.map_err(Into::into)
24 }
25
26 pub async fn ensure_genesis_in_place(&mut self) -> Result<(), ClientError> {
30 if self.rpc_api.has_genesis_commitment().is_some() {
31 return Ok(());
32 }
33
34 let (genesis, _) = self
35 .rpc_api
36 .get_block_header_by_number(Some(BlockNumber::GENESIS), false)
37 .await?;
38
39 let blank_mmr_peaks = MmrPeaks::new(Forest::empty(), vec![])
40 .expect("Blank MmrPeaks should not fail to instantiate");
41 self.store.insert_block_header(&genesis, blank_mmr_peaks, false).await?;
42 self.rpc_api.set_genesis_commitment(genesis.commitment()).await?;
43 Ok(())
44 }
45
46 pub async fn get_current_partial_mmr(&self) -> Result<PartialMmr, ClientError> {
48 self.store.get_current_partial_mmr().await.map_err(Into::into)
49 }
50
51 pub(crate) async fn get_and_store_authenticated_block(
60 &self,
61 block_num: BlockNumber,
62 current_partial_mmr: &mut PartialMmr,
63 ) -> Result<BlockHeader, ClientError> {
64 if current_partial_mmr.is_tracked(block_num.as_usize()) {
65 warn!("Current partial MMR already contains the requested data");
66 let (block_header, _) = self
67 .store
68 .get_block_header_by_num(block_num)
69 .await?
70 .expect("Block header should be tracked");
71 return Ok(block_header);
72 }
73
74 let (block_header, path_nodes) =
76 fetch_block_header(self.rpc_api.clone(), block_num, current_partial_mmr).await?;
77 let tracked_nodes = authenticated_block_nodes(&block_header, path_nodes);
78
79 self.store
81 .insert_block_header(&block_header, current_partial_mmr.peaks(), true)
82 .await?;
83 self.store.insert_partial_blockchain_nodes(&tracked_nodes).await?;
84
85 Ok(block_header)
86 }
87}
88
89pub(crate) fn adjust_merkle_path_for_forest(
101 merkle_path: &MerklePath,
102 block_num: BlockNumber,
103 forest: Forest,
104) -> Vec<(InOrderIndex, Word)> {
105 let expected_path_len = forest
106 .leaf_to_corresponding_tree(block_num.as_usize())
107 .expect("forest includes block number") as usize;
108
109 let mut idx = InOrderIndex::from_leaf_pos(block_num.as_usize());
110 let mut path_nodes = Vec::with_capacity(expected_path_len);
111
112 for node in merkle_path.nodes().iter().take(expected_path_len) {
113 path_nodes.push((idx.sibling(), *node));
114 idx = idx.parent();
115 }
116
117 path_nodes
118}
119
120fn authenticated_block_nodes(
121 block_header: &BlockHeader,
122 mut path_nodes: Vec<(InOrderIndex, Word)>,
123) -> Vec<(InOrderIndex, Word)> {
124 let mut nodes = Vec::with_capacity(path_nodes.len() + 1);
125 nodes.push((
126 InOrderIndex::from_leaf_pos(block_header.block_num().as_usize()),
127 block_header.commitment(),
128 ));
129 nodes.append(&mut path_nodes);
130 nodes
131}
132
133pub(crate) async fn fetch_block_header(
134 rpc_api: Arc<dyn NodeRpcClient>,
135 block_num: BlockNumber,
136 current_partial_mmr: &mut PartialMmr,
137) -> Result<(BlockHeader, Vec<(InOrderIndex, Word)>), ClientError> {
138 let (block_header, mmr_proof) = rpc_api.get_block_header_with_proof(block_num).await?;
139
140 let path_nodes = adjust_merkle_path_for_forest(
143 mmr_proof.merkle_path(),
144 block_num,
145 current_partial_mmr.forest(),
146 );
147
148 let merkle_path = MerklePath::new(path_nodes.iter().map(|(_, n)| *n).collect());
149
150 current_partial_mmr
151 .track(block_num.as_usize(), block_header.commitment(), &merkle_path)
152 .map_err(StoreError::MmrError)?;
153
154 Ok((block_header, path_nodes))
155}
156
157#[cfg(test)]
158mod tests {
159 use miden_protocol::block::{BlockHeader, BlockNumber};
160 use miden_protocol::crypto::merkle::MerklePath;
161 use miden_protocol::crypto::merkle::mmr::{Forest, InOrderIndex, Mmr, PartialMmr};
162 use miden_protocol::transaction::TransactionKernel;
163 use miden_protocol::{Felt, Word};
164
165 use super::{adjust_merkle_path_for_forest, authenticated_block_nodes};
166
167 fn word(n: u64) -> Word {
168 Word::new([Felt::new(n), Felt::new(0), Felt::new(0), Felt::new(0)])
169 }
170
171 #[test]
172 fn adjust_merkle_path_truncates_to_forest_bounds() {
173 let forest = Forest::new(5);
174 let block_num = BlockNumber::from(4u32);
176 let path = MerklePath::new(vec![word(1), word(2), word(3)]);
177
178 let adjusted = adjust_merkle_path_for_forest(&path, block_num, forest);
179 assert!(adjusted.is_empty());
181 }
182
183 #[test]
184 fn adjust_merkle_path_keeps_proof_valid_for_smaller_forest() {
185 let mut mmr = Mmr::new();
188 for value in 0u64..8 {
189 mmr.add(word(value));
190 }
191
192 let large_forest = Forest::new(8);
193 let small_forest = Forest::new(5);
194 let leaf_pos = 4usize;
195 let block_num = BlockNumber::from(u32::try_from(leaf_pos).unwrap());
196
197 let proof = mmr.open_at(leaf_pos, large_forest).expect("valid proof");
198 let adjusted_nodes =
199 adjust_merkle_path_for_forest(proof.merkle_path(), block_num, small_forest);
200 let adjusted_path = MerklePath::new(adjusted_nodes.iter().map(|(_, n)| *n).collect());
201
202 let peaks = mmr.peaks_at(small_forest).unwrap();
203 let mut partial = PartialMmr::from_peaks(peaks);
204 let leaf = mmr.get(leaf_pos).expect("leaf exists");
205
206 partial
207 .track(leaf_pos, leaf, &adjusted_path)
208 .expect("adjusted path should verify against smaller forest peaks");
209 }
210
211 #[test]
212 fn adjust_merkle_path_correct_indices() {
213 let forest = Forest::new(6);
215 let block_num = BlockNumber::from(1u32);
217 let nodes = vec![word(10), word(11), word(12), word(13)];
218 let path = MerklePath::new(nodes.clone());
219
220 let adjusted = adjust_merkle_path_for_forest(&path, block_num, forest);
221
222 assert_eq!(adjusted.len(), 2);
223 assert_eq!(adjusted[0].1, nodes[0]);
224 assert_eq!(adjusted[1].1, nodes[1]);
225
226 let mut idx = InOrderIndex::from_leaf_pos(1);
227 let expected0 = idx.sibling();
228 idx = idx.parent();
229 let expected1 = idx.sibling();
230
231 assert_eq!(adjusted[0].0, expected0);
232 assert_eq!(adjusted[1].0, expected1);
233 }
234
235 #[test]
236 fn adjust_path_limit_correct_when_siblings_in_bounds() {
237 let large_leaves = 8usize;
240 let small_leaves = 7usize;
241 let leaf_pos = 2usize;
242 let mut mmr = Mmr::new();
243 for value in 0u64..large_leaves as u64 {
244 mmr.add(word(value));
245 }
246
247 let small_forest = Forest::new(small_leaves);
248 let proof = mmr.open_at(leaf_pos, Forest::new(large_leaves)).expect("valid proof");
249 let expected_depth =
250 small_forest.leaf_to_corresponding_tree(leaf_pos).expect("leaf is in forest") as usize;
251
252 let mut idx = InOrderIndex::from_leaf_pos(leaf_pos);
255 for _ in 0..expected_depth {
256 idx = idx.parent();
257 }
258 let next_sibling = idx.sibling();
259 let rightmost = InOrderIndex::from_leaf_pos(small_leaves - 1);
260 assert!(next_sibling <= rightmost);
261 assert!(proof.merkle_path().depth() as usize > expected_depth);
262
263 let adjusted = adjust_merkle_path_for_forest(
264 proof.merkle_path(),
265 BlockNumber::from(u32::try_from(leaf_pos).unwrap()),
266 small_forest,
267 );
268 assert_eq!(adjusted.len(), expected_depth);
269 }
270
271 #[test]
272 fn authenticated_block_nodes_include_leaf_commitment() {
273 let block_header = BlockHeader::mock(4, None, None, &[], TransactionKernel.to_commitment());
274 let path_nodes = vec![
275 (InOrderIndex::from_leaf_pos(4).sibling(), word(10)),
276 (InOrderIndex::from_leaf_pos(4).parent().sibling(), word(11)),
277 ];
278
279 let nodes = authenticated_block_nodes(&block_header, path_nodes.clone());
280
281 assert_eq!(nodes[0], (InOrderIndex::from_leaf_pos(4), block_header.commitment()));
282 assert_eq!(&nodes[1..], path_nodes.as_slice());
283 }
284}