miden_objects/block/
note_tree.rs1use alloc::string::ToString;
2
3use miden_crypto::merkle::SparseMerklePath;
4
5use crate::batch::BatchNoteTree;
6use crate::crypto::merkle::{LeafIndex, MerkleError, SimpleSmt};
7use crate::note::{NoteId, NoteMetadata, compute_note_commitment};
8use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
9use crate::{
10    BLOCK_NOTE_TREE_DEPTH,
11    MAX_BATCHES_PER_BLOCK,
12    MAX_OUTPUT_NOTES_PER_BATCH,
13    MAX_OUTPUT_NOTES_PER_BLOCK,
14    Word,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct BlockNoteTree(SimpleSmt<BLOCK_NOTE_TREE_DEPTH>);
24
25impl BlockNoteTree {
26    pub fn with_entries(
39        entries: impl IntoIterator<Item = (BlockNoteIndex, NoteId, NoteMetadata)>,
40    ) -> Result<Self, MerkleError> {
41        let leaves = entries.into_iter().map(|(index, note_id, metadata)| {
42            (index.leaf_index_value() as u64, compute_note_commitment(note_id, &metadata))
43        });
44
45        SimpleSmt::with_leaves(leaves).map(Self)
46    }
47
48    pub fn empty() -> Self {
50        Self(SimpleSmt::new().expect("depth should be 16 and thus > 0 and <= 64"))
51    }
52
53    pub fn insert_batch_note_subtree(
61        &mut self,
62        batch_idx: u64,
63        batch_note_tree: BatchNoteTree,
64    ) -> Result<(), MerkleError> {
65        self.0.set_subtree(batch_idx, batch_note_tree.into_smt()).map(|_| ())
69    }
70
71    pub fn root(&self) -> Word {
73        self.0.root()
74    }
75
76    pub fn open(&self, index: BlockNoteIndex) -> SparseMerklePath {
78        self.0.open(&index.leaf_index()).path
80    }
81
82    pub fn num_notes(&self) -> usize {
84        self.0.num_leaves()
85    }
86
87    pub fn is_empty(&self) -> bool {
89        self.0.is_empty()
90    }
91}
92
93impl Default for BlockNoteTree {
94    fn default() -> Self {
95        Self(SimpleSmt::new().expect("Unreachable"))
96    }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub struct BlockNoteIndex {
105    batch_idx: usize,
106    note_idx_in_batch: usize,
107}
108
109impl BlockNoteIndex {
110    pub fn new(batch_idx: usize, note_idx_in_batch: usize) -> Option<Self> {
117        if batch_idx >= MAX_BATCHES_PER_BLOCK || note_idx_in_batch >= MAX_OUTPUT_NOTES_PER_BATCH {
118            return None;
119        }
120
121        Some(Self { batch_idx, note_idx_in_batch })
122    }
123
124    pub fn batch_idx(&self) -> usize {
126        self.batch_idx
127    }
128
129    pub fn note_idx_in_batch(&self) -> usize {
131        self.note_idx_in_batch
132    }
133
134    pub fn leaf_index(&self) -> LeafIndex<BLOCK_NOTE_TREE_DEPTH> {
136        LeafIndex::new(
137            (self.batch_idx() * MAX_OUTPUT_NOTES_PER_BATCH + self.note_idx_in_batch()) as u64,
138        )
139        .expect("Unreachable: Input values must be valid at this point")
140    }
141
142    pub fn leaf_index_value(&self) -> u16 {
144        const _: () = assert!(
145            MAX_OUTPUT_NOTES_PER_BLOCK <= u16::MAX as usize + 1,
146            "Any note index is expected to fit in `u16`"
147        );
148
149        self.leaf_index()
150            .value()
151            .try_into()
152            .expect("Unreachable: Input values must be valid at this point")
153    }
154}
155
156impl Serializable for BlockNoteTree {
160    fn write_into<W: ByteWriter>(&self, target: &mut W) {
161        target.write_u32(self.0.num_leaves() as u32);
162        target.write_many(self.0.leaves());
163    }
164}
165
166impl Deserializable for BlockNoteTree {
167    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
168        let count = source.read_u32()?;
169        let leaves = source.read_many(count as usize)?;
170
171        SimpleSmt::with_leaves(leaves)
172            .map(Self)
173            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use miden_crypto::merkle::SimpleSmt;
180    use miden_crypto::utils::{Deserializable, Serializable};
181
182    use super::BlockNoteTree;
183    use crate::Word;
184
185    #[test]
186    fn test_serialization() {
187        let data = core::iter::repeat(())
188            .enumerate()
189            .map(|(idx, ())| (idx as u64, Word::from([1, 0, 1, idx as u32])))
190            .take(100);
191        let initial_tree = BlockNoteTree(SimpleSmt::with_leaves(data).unwrap());
192
193        let serialized = initial_tree.to_bytes();
194        let deserialized_tree = BlockNoteTree::read_from_bytes(&serialized).unwrap();
195
196        assert_eq!(deserialized_tree, initial_tree);
197    }
198}