miden_protocol/block/
note_tree.rs1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use miden_crypto::merkle::SparseMerklePath;
5
6use crate::batch::BatchNoteTree;
7use crate::crypto::merkle::MerkleError;
8use crate::crypto::merkle::smt::{LeafIndex, SimpleSmt};
9use crate::note::{NoteId, NoteMetadata, compute_note_commitment};
10use crate::utils::serde::{
11 ByteReader,
12 ByteWriter,
13 Deserializable,
14 DeserializationError,
15 Serializable,
16};
17use crate::{
18 BLOCK_NOTE_TREE_DEPTH,
19 MAX_BATCHES_PER_BLOCK,
20 MAX_OUTPUT_NOTES_PER_BATCH,
21 MAX_OUTPUT_NOTES_PER_BLOCK,
22 Word,
23};
24
25#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct BlockNoteTree(SimpleSmt<BLOCK_NOTE_TREE_DEPTH>);
32
33impl BlockNoteTree {
34 pub fn with_entries<'metadata>(
47 entries: impl IntoIterator<Item = (BlockNoteIndex, NoteId, &'metadata NoteMetadata)>,
48 ) -> Result<Self, MerkleError> {
49 let leaves = entries.into_iter().map(|(index, note_id, metadata)| {
50 (index.leaf_index_value() as u64, compute_note_commitment(note_id, metadata))
51 });
52
53 SimpleSmt::with_leaves(leaves).map(Self)
54 }
55
56 pub fn empty() -> Self {
58 Self(SimpleSmt::new().expect("depth should be 16 and thus > 0 and <= 64"))
59 }
60
61 pub fn insert_batch_note_subtree(
69 &mut self,
70 batch_idx: u64,
71 batch_note_tree: BatchNoteTree,
72 ) -> Result<(), MerkleError> {
73 self.0.set_subtree(batch_idx, batch_note_tree.into_smt()).map(|_| ())
77 }
78
79 pub fn root(&self) -> Word {
81 self.0.root()
82 }
83
84 pub fn open(&self, index: BlockNoteIndex) -> SparseMerklePath {
86 self.0.open(&index.leaf_index()).path
88 }
89
90 pub fn num_notes(&self) -> usize {
92 self.0.num_leaves()
93 }
94
95 pub fn is_empty(&self) -> bool {
97 self.0.is_empty()
98 }
99}
100
101impl Default for BlockNoteTree {
102 fn default() -> Self {
103 Self(SimpleSmt::new().expect("Unreachable"))
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub struct BlockNoteIndex {
113 batch_idx: usize,
114 note_idx_in_batch: usize,
115}
116
117impl BlockNoteIndex {
118 pub fn new(batch_idx: usize, note_idx_in_batch: usize) -> Option<Self> {
125 if batch_idx >= MAX_BATCHES_PER_BLOCK || note_idx_in_batch >= MAX_OUTPUT_NOTES_PER_BATCH {
126 return None;
127 }
128
129 Some(Self { batch_idx, note_idx_in_batch })
130 }
131
132 pub fn batch_idx(&self) -> usize {
134 self.batch_idx
135 }
136
137 pub fn note_idx_in_batch(&self) -> usize {
139 self.note_idx_in_batch
140 }
141
142 pub fn leaf_index(&self) -> LeafIndex<BLOCK_NOTE_TREE_DEPTH> {
144 LeafIndex::new(
145 (self.batch_idx() * MAX_OUTPUT_NOTES_PER_BATCH + self.note_idx_in_batch()) as u64,
146 )
147 .expect("Unreachable: Input values must be valid at this point")
148 }
149
150 pub fn leaf_index_value(&self) -> u16 {
152 const _: () = assert!(
153 MAX_OUTPUT_NOTES_PER_BLOCK <= u16::MAX as usize + 1,
154 "Any note index is expected to fit in `u16`"
155 );
156
157 self.leaf_index()
158 .value()
159 .try_into()
160 .expect("Unreachable: Input values must be valid at this point")
161 }
162}
163
164impl Serializable for BlockNoteTree {
168 fn write_into<W: ByteWriter>(&self, target: &mut W) {
169 target.write_u32(self.0.num_leaves() as u32);
170 target.write_many(self.0.leaves());
171 }
172}
173
174impl Deserializable for BlockNoteTree {
175 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
176 let count = source.read_u32()?;
177 let leaves = source.read_many_iter(count as usize)?.collect::<Result<Vec<_>, _>>()?;
178
179 SimpleSmt::with_leaves(leaves)
180 .map(Self)
181 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use miden_crypto::merkle::smt::SimpleSmt;
188 use miden_crypto::utils::{Deserializable, Serializable};
189
190 use super::BlockNoteTree;
191 use crate::Word;
192
193 #[test]
194 fn test_serialization() {
195 let data = core::iter::repeat(())
196 .enumerate()
197 .map(|(idx, ())| (idx as u64, Word::from([1, 0, 1, idx as u32])))
198 .take(100);
199 let initial_tree = BlockNoteTree(SimpleSmt::with_leaves(data).unwrap());
200
201 let serialized = initial_tree.to_bytes();
202 let deserialized_tree = BlockNoteTree::read_from_bytes(&serialized).unwrap();
203
204 assert_eq!(deserialized_tree, initial_tree);
205 }
206}