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::NoteHeader;
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)]
29pub struct BlockNoteTree(SimpleSmt<BLOCK_NOTE_TREE_DEPTH>);
30
31impl BlockNoteTree {
32 pub fn with_entries<'a>(
46 entries: impl IntoIterator<Item = (BlockNoteIndex, &'a NoteHeader)>,
47 ) -> Result<Self, MerkleError> {
48 let leaves = entries
49 .into_iter()
50 .map(|(index, header)| (index.leaf_index_value() as u64, header.id().as_word()));
51
52 SimpleSmt::with_leaves(leaves).map(Self)
53 }
54
55 pub fn empty() -> Self {
57 Self(SimpleSmt::new().expect("depth should be 16 and thus > 0 and <= 64"))
58 }
59
60 pub fn insert_batch_note_subtree(
68 &mut self,
69 batch_idx: u64,
70 batch_note_tree: BatchNoteTree,
71 ) -> Result<(), MerkleError> {
72 self.0.set_subtree(batch_idx, batch_note_tree.into_smt()).map(|_| ())
76 }
77
78 pub fn root(&self) -> Word {
80 self.0.root()
81 }
82
83 pub fn open(&self, index: BlockNoteIndex) -> SparseMerklePath {
85 self.0.open(&index.leaf_index()).path
87 }
88
89 pub fn num_notes(&self) -> usize {
91 self.0.num_leaves()
92 }
93
94 pub fn is_empty(&self) -> bool {
96 self.0.is_empty()
97 }
98}
99
100impl Default for BlockNoteTree {
101 fn default() -> Self {
102 Self(SimpleSmt::new().expect("Unreachable"))
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub struct BlockNoteIndex {
112 batch_idx: usize,
113 note_idx_in_batch: usize,
114}
115
116impl BlockNoteIndex {
117 pub fn new(batch_idx: usize, note_idx_in_batch: usize) -> Option<Self> {
124 if batch_idx >= MAX_BATCHES_PER_BLOCK || note_idx_in_batch >= MAX_OUTPUT_NOTES_PER_BATCH {
125 return None;
126 }
127
128 Some(Self { batch_idx, note_idx_in_batch })
129 }
130
131 pub fn batch_idx(&self) -> usize {
133 self.batch_idx
134 }
135
136 pub fn note_idx_in_batch(&self) -> usize {
138 self.note_idx_in_batch
139 }
140
141 pub fn leaf_index(&self) -> LeafIndex<BLOCK_NOTE_TREE_DEPTH> {
143 LeafIndex::new(
144 (self.batch_idx() * MAX_OUTPUT_NOTES_PER_BATCH + self.note_idx_in_batch()) as u64,
145 )
146 .expect("Unreachable: Input values must be valid at this point")
147 }
148
149 pub fn leaf_index_value(&self) -> u16 {
151 const _: () = assert!(
152 MAX_OUTPUT_NOTES_PER_BLOCK <= u16::MAX as usize + 1,
153 "Any note index is expected to fit in `u16`"
154 );
155
156 self.leaf_index()
157 .position()
158 .try_into()
159 .expect("Unreachable: Input values must be valid at this point")
160 }
161}
162
163impl Serializable for BlockNoteTree {
167 fn write_into<W: ByteWriter>(&self, target: &mut W) {
168 target.write_u32(self.0.num_leaves() as u32);
169 target.write_many(self.0.leaves());
170 }
171}
172
173impl Deserializable for BlockNoteTree {
174 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
175 let count = source.read_u32()?;
176 let leaves = source.read_many_iter(count as usize)?.collect::<Result<Vec<_>, _>>()?;
177
178 SimpleSmt::with_leaves(leaves)
179 .map(Self)
180 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use miden_crypto::merkle::smt::SimpleSmt;
187 use miden_crypto::utils::{Deserializable, Serializable};
188
189 use super::BlockNoteTree;
190 use crate::Word;
191
192 #[test]
193 fn test_serialization() {
194 let data = core::iter::repeat(())
195 .enumerate()
196 .map(|(idx, ())| (idx as u64, Word::from([1, 0, 1, idx as u32])))
197 .take(100);
198 let initial_tree = BlockNoteTree(SimpleSmt::with_leaves(data).unwrap());
199
200 let serialized = initial_tree.to_bytes();
201 let deserialized_tree = BlockNoteTree::read_from_bytes(&serialized).unwrap();
202
203 assert_eq!(deserialized_tree, initial_tree);
204 }
205}