use alloc::string::ToString;
use miden_crypto::merkle::SparseMerklePath;
use crate::batch::BatchNoteTree;
use crate::crypto::merkle::{LeafIndex, MerkleError, SimpleSmt};
use crate::note::{NoteId, NoteMetadata, compute_note_commitment};
use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
use crate::{
BLOCK_NOTE_TREE_DEPTH,
MAX_BATCHES_PER_BLOCK,
MAX_OUTPUT_NOTES_PER_BATCH,
MAX_OUTPUT_NOTES_PER_BLOCK,
Word,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockNoteTree(SimpleSmt<BLOCK_NOTE_TREE_DEPTH>);
impl BlockNoteTree {
pub fn with_entries(
entries: impl IntoIterator<Item = (BlockNoteIndex, NoteId, NoteMetadata)>,
) -> Result<Self, MerkleError> {
let leaves = entries.into_iter().map(|(index, note_id, metadata)| {
(index.leaf_index_value() as u64, compute_note_commitment(note_id, &metadata))
});
SimpleSmt::with_leaves(leaves).map(Self)
}
pub fn empty() -> Self {
Self(SimpleSmt::new().expect("depth should be 16 and thus > 0 and <= 64"))
}
pub fn insert_batch_note_subtree(
&mut self,
batch_idx: u64,
batch_note_tree: BatchNoteTree,
) -> Result<(), MerkleError> {
self.0.set_subtree(batch_idx, batch_note_tree.into_smt()).map(|_| ())
}
pub fn root(&self) -> Word {
self.0.root()
}
pub fn open(&self, index: BlockNoteIndex) -> SparseMerklePath {
self.0.open(&index.leaf_index()).path
}
pub fn num_notes(&self) -> usize {
self.0.num_leaves()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl Default for BlockNoteTree {
fn default() -> Self {
Self(SimpleSmt::new().expect("Unreachable"))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlockNoteIndex {
batch_idx: usize,
note_idx_in_batch: usize,
}
impl BlockNoteIndex {
pub fn new(batch_idx: usize, note_idx_in_batch: usize) -> Option<Self> {
if batch_idx >= MAX_BATCHES_PER_BLOCK || note_idx_in_batch >= MAX_OUTPUT_NOTES_PER_BATCH {
return None;
}
Some(Self { batch_idx, note_idx_in_batch })
}
pub fn batch_idx(&self) -> usize {
self.batch_idx
}
pub fn note_idx_in_batch(&self) -> usize {
self.note_idx_in_batch
}
pub fn leaf_index(&self) -> LeafIndex<BLOCK_NOTE_TREE_DEPTH> {
LeafIndex::new(
(self.batch_idx() * MAX_OUTPUT_NOTES_PER_BATCH + self.note_idx_in_batch()) as u64,
)
.expect("Unreachable: Input values must be valid at this point")
}
pub fn leaf_index_value(&self) -> u16 {
const _: () = assert!(
MAX_OUTPUT_NOTES_PER_BLOCK <= u16::MAX as usize + 1,
"Any note index is expected to fit in `u16`"
);
self.leaf_index()
.value()
.try_into()
.expect("Unreachable: Input values must be valid at this point")
}
}
impl Serializable for BlockNoteTree {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u32(self.0.num_leaves() as u32);
target.write_many(self.0.leaves());
}
}
impl Deserializable for BlockNoteTree {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let count = source.read_u32()?;
let leaves = source.read_many(count as usize)?;
SimpleSmt::with_leaves(leaves)
.map(Self)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[cfg(test)]
mod tests {
use miden_crypto::merkle::SimpleSmt;
use miden_crypto::utils::{Deserializable, Serializable};
use super::BlockNoteTree;
use crate::Word;
#[test]
fn test_serialization() {
let data = core::iter::repeat(())
.enumerate()
.map(|(idx, ())| (idx as u64, Word::from([1, 0, 1, idx as u32])))
.take(100);
let initial_tree = BlockNoteTree(SimpleSmt::with_leaves(data).unwrap());
let serialized = initial_tree.to_bytes();
let deserialized_tree = BlockNoteTree::read_from_bytes(&serialized).unwrap();
assert_eq!(deserialized_tree, initial_tree);
}
}