miden_objects/block/
note_tree.rs1use alloc::string::ToString;
2
3use miden_crypto::{
4 hash::rpo::RpoDigest,
5 merkle::{LeafIndex, MerkleError, MerklePath, SimpleSmt},
6};
7
8use crate::{
9 note::{compute_note_hash, NoteId, NoteMetadata},
10 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
11 BlockError, BLOCK_NOTE_TREE_DEPTH, MAX_BATCHES_PER_BLOCK, MAX_OUTPUT_NOTES_PER_BATCH,
12 MAX_OUTPUT_NOTES_PER_BLOCK,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct BlockNoteTree(SimpleSmt<BLOCK_NOTE_TREE_DEPTH>);
22
23impl BlockNoteTree {
24 pub fn with_entries(
37 entries: impl IntoIterator<Item = (BlockNoteIndex, NoteId, NoteMetadata)>,
38 ) -> Result<Self, MerkleError> {
39 let leaves = entries.into_iter().map(|(index, note_id, metadata)| {
40 (index.leaf_index_value() as u64, compute_note_hash(note_id, &metadata).into())
41 });
42
43 SimpleSmt::with_leaves(leaves).map(Self)
44 }
45
46 pub fn root(&self) -> RpoDigest {
48 self.0.root()
49 }
50
51 pub fn get_note_path(&self, index: BlockNoteIndex) -> MerklePath {
53 self.0.open(&index.leaf_index()).path
55 }
56}
57
58impl Default for BlockNoteTree {
59 fn default() -> Self {
60 Self(SimpleSmt::new().expect("Unreachable"))
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct BlockNoteIndex {
67 batch_idx: usize,
68 note_idx_in_batch: usize,
69}
70
71impl BlockNoteIndex {
72 pub fn new(batch_idx: usize, note_idx_in_batch: usize) -> Result<Self, BlockError> {
74 if note_idx_in_batch >= MAX_OUTPUT_NOTES_PER_BATCH {
75 return Err(BlockError::TooManyNotesInBatch(note_idx_in_batch));
76 }
77 if batch_idx >= MAX_BATCHES_PER_BLOCK {
78 return Err(BlockError::TooManyTransactionBatches(batch_idx));
79 }
80
81 Ok(Self { batch_idx, note_idx_in_batch })
82 }
83
84 pub fn batch_idx(&self) -> usize {
86 self.batch_idx
87 }
88
89 pub fn note_idx_in_batch(&self) -> usize {
91 self.note_idx_in_batch
92 }
93
94 pub fn leaf_index(&self) -> LeafIndex<BLOCK_NOTE_TREE_DEPTH> {
96 LeafIndex::new(
97 (self.batch_idx() * MAX_OUTPUT_NOTES_PER_BATCH + self.note_idx_in_batch()) as u64,
98 )
99 .expect("Unreachable: Input values must be valid at this point")
100 }
101
102 pub fn leaf_index_value(&self) -> u16 {
104 const _: () = assert!(
105 MAX_OUTPUT_NOTES_PER_BLOCK <= u16::MAX as usize + 1,
106 "Any note index is expected to fit in `u16`"
107 );
108
109 self.leaf_index()
110 .value()
111 .try_into()
112 .expect("Unreachable: Input values must be valid at this point")
113 }
114}
115
116impl Serializable for BlockNoteTree {
120 fn write_into<W: ByteWriter>(&self, target: &mut W) {
121 target.write_u32(self.0.num_leaves() as u32);
122 target.write_many(self.0.leaves());
123 }
124}
125
126impl Deserializable for BlockNoteTree {
127 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
128 let count = source.read_u32()?;
129 let leaves = source.read_many(count as usize)?;
130
131 SimpleSmt::with_leaves(leaves)
132 .map(Self)
133 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use miden_crypto::{
140 merkle::SimpleSmt,
141 utils::{Deserializable, Serializable},
142 Felt, ONE, ZERO,
143 };
144
145 use super::BlockNoteTree;
146
147 #[test]
148 fn test_serialization() {
149 let data = core::iter::repeat(())
150 .enumerate()
151 .map(|(idx, ())| (idx as u64, [ONE, ZERO, ONE, Felt::new(idx as u64)]))
152 .take(100);
153 let initial_tree = BlockNoteTree(SimpleSmt::with_leaves(data).unwrap());
154
155 let serialized = initial_tree.to_bytes();
156 let deserialized_tree = BlockNoteTree::read_from_bytes(&serialized).unwrap();
157
158 assert_eq!(deserialized_tree, initial_tree);
159 }
160}