Skip to main content

miden_protocol/block/nullifier_tree/
mod.rs

1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use crate::block::BlockNumber;
5use crate::crypto::merkle::MerkleError;
6use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt};
7use crate::errors::NullifierTreeError;
8use crate::note::Nullifier;
9use crate::utils::serde::{
10    ByteReader,
11    ByteWriter,
12    Deserializable,
13    DeserializationError,
14    Serializable,
15};
16use crate::{Felt, Word};
17
18mod backend;
19pub use backend::NullifierTreeBackend;
20
21mod witness;
22pub use witness::NullifierWitness;
23
24mod partial;
25pub use partial::PartialNullifierTree;
26
27// NULLIFIER TREE
28// ================================================================================================
29
30/// The sparse merkle tree of all nullifiers in the blockchain.
31///
32/// A nullifier can only ever be spent once and its value in the tree is the block number at which
33/// it was spent.
34///
35/// The tree guarantees that once a nullifier has been inserted into the tree, its block number does
36/// not change. Note that inserting the nullifier multiple times with the same block number is
37/// valid.
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct NullifierTree<Backend = Smt> {
40    smt: Backend,
41}
42
43impl<Backend> Default for NullifierTree<Backend>
44where
45    Backend: Default,
46{
47    fn default() -> Self {
48        Self { smt: Default::default() }
49    }
50}
51
52impl<Backend> NullifierTree<Backend>
53where
54    Backend: NullifierTreeBackend<Error = MerkleError>,
55{
56    // CONSTANTS
57    // --------------------------------------------------------------------------------------------
58
59    /// The depth of the nullifier tree.
60    pub const DEPTH: u8 = SMT_DEPTH;
61
62    // CONSTRUCTORS
63    // --------------------------------------------------------------------------------------------
64
65    /// Creates a new `NullifierTree` from its inner representation.
66    ///
67    /// # Invariants
68    ///
69    /// See the documentation on [`NullifierTreeBackend`] trait documentation.
70    pub fn new_unchecked(backend: Backend) -> Self {
71        NullifierTree { smt: backend }
72    }
73
74    // PUBLIC ACCESSORS
75    // --------------------------------------------------------------------------------------------
76
77    /// Returns the root of the nullifier SMT.
78    pub fn root(&self) -> Word {
79        self.smt.root()
80    }
81
82    /// Returns the number of spent nullifiers in this tree.
83    pub fn num_nullifiers(&self) -> usize {
84        self.smt.num_entries()
85    }
86
87    /// Returns an iterator over the nullifiers and their block numbers in the tree.
88    pub fn entries(&self) -> impl Iterator<Item = (Nullifier, BlockNumber)> {
89        self.smt.entries().map(|(nullifier, value)| {
90            (
91                Nullifier::from_raw(nullifier),
92                NullifierBlock::new(value)
93                    .expect("SMT should only store valid NullifierBlocks")
94                    .into(),
95            )
96        })
97    }
98
99    /// Returns a [`NullifierWitness`] of the leaf associated with the `nullifier`.
100    ///
101    /// Conceptually, such a witness is a Merkle path to the leaf, as well as the leaf itself.
102    ///
103    /// This witness is a proof of the current block number of the given nullifier. If that block
104    /// number is zero, it proves that the nullifier is unspent.
105    pub fn open(&self, nullifier: &Nullifier) -> NullifierWitness {
106        NullifierWitness::new(self.smt.open(&nullifier.as_word()))
107    }
108
109    /// Returns the block number for the given nullifier or `None` if the nullifier wasn't spent
110    /// yet.
111    pub fn get_block_num(&self, nullifier: &Nullifier) -> Option<BlockNumber> {
112        let nullifier_block = self.smt.get_value(&nullifier.as_word());
113        if nullifier_block.is_unspent() {
114            return None;
115        }
116
117        Some(nullifier_block.into())
118    }
119
120    /// Computes a mutation set resulting from inserting the provided nullifiers into this nullifier
121    /// tree.
122    ///
123    /// # Errors
124    ///
125    /// Returns an error if:
126    /// - a nullifier in the provided iterator was already spent.
127    pub fn compute_mutations<I>(
128        &self,
129        nullifiers: impl IntoIterator<Item = (Nullifier, BlockNumber), IntoIter = I>,
130    ) -> Result<NullifierMutationSet, NullifierTreeError>
131    where
132        I: Iterator<Item = (Nullifier, BlockNumber)> + Clone,
133    {
134        let nullifiers = nullifiers.into_iter();
135        for (nullifier, _) in nullifiers.clone() {
136            if self.get_block_num(&nullifier).is_some() {
137                return Err(NullifierTreeError::NullifierAlreadySpent(nullifier));
138            }
139        }
140
141        let mutation_set = self
142            .smt
143            .compute_mutations(
144                nullifiers
145                    .into_iter()
146                    .map(|(nullifier, block_num)| {
147                        (nullifier.as_word(), NullifierBlock::from(block_num).into())
148                    })
149                    .collect::<Vec<_>>(),
150            )
151            .map_err(NullifierTreeError::ComputeMutations)?;
152
153        Ok(NullifierMutationSet::new(mutation_set))
154    }
155
156    // PUBLIC MUTATORS
157    // --------------------------------------------------------------------------------------------
158
159    /// Marks the given nullifier as spent at the given block number.
160    ///
161    /// # Errors
162    ///
163    /// Returns an error if:
164    /// - the nullifier was already spent.
165    pub fn mark_spent(
166        &mut self,
167        nullifier: Nullifier,
168        block_num: BlockNumber,
169    ) -> Result<(), NullifierTreeError> {
170        let prev_nullifier_value = self
171            .smt
172            .insert(nullifier.as_word(), NullifierBlock::from(block_num))
173            .map_err(NullifierTreeError::MaxLeafEntriesExceeded)?;
174
175        if prev_nullifier_value.is_spent() {
176            Err(NullifierTreeError::NullifierAlreadySpent(nullifier))
177        } else {
178            Ok(())
179        }
180    }
181
182    /// Applies mutations to the nullifier tree.
183    ///
184    /// # Errors
185    ///
186    /// Returns an error if:
187    /// - `mutations` was computed on a tree with a different root than this one.
188    pub fn apply_mutations(
189        &mut self,
190        mutations: NullifierMutationSet,
191    ) -> Result<(), NullifierTreeError> {
192        self.smt
193            .apply_mutations(mutations.into_mutation_set())
194            .map_err(NullifierTreeError::TreeRootConflict)
195    }
196}
197
198// SERIALIZATION
199// ================================================================================================
200
201impl Serializable for NullifierTree {
202    fn write_into<W: ByteWriter>(&self, target: &mut W) {
203        self.entries().collect::<Vec<_>>().write_into(target);
204    }
205}
206
207impl Deserializable for NullifierTree {
208    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
209        let entries = Vec::<(Nullifier, BlockNumber)>::read_from(source)?;
210        Self::with_entries(entries)
211            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
212    }
213}
214
215// NULLIFIER MUTATION SET
216// ================================================================================================
217
218/// A newtype wrapper around a [`MutationSet`] for use in the [`NullifierTree`].
219///
220/// It guarantees that applying the contained mutations will result in a nullifier tree where
221/// nullifier's block numbers are not updated (except if they were unspent before), ensuring that
222/// nullifiers are only spent once.
223///
224/// It is returned by and used in methods on the [`NullifierTree`].
225#[derive(Debug, Clone, PartialEq, Eq)]
226pub struct NullifierMutationSet {
227    mutation_set: MutationSet<SMT_DEPTH, Word, Word>,
228}
229
230impl NullifierMutationSet {
231    // CONSTRUCTORS
232    // --------------------------------------------------------------------------------------------
233
234    /// Creates a new [`NullifierMutationSet`] from the provided raw mutation set.
235    fn new(mutation_set: MutationSet<SMT_DEPTH, Word, Word>) -> Self {
236        Self { mutation_set }
237    }
238
239    // PUBLIC ACCESSORS
240    // --------------------------------------------------------------------------------------------
241
242    /// Returns a reference to the underlying [`MutationSet`].
243    pub fn as_mutation_set(&self) -> &MutationSet<SMT_DEPTH, Word, Word> {
244        &self.mutation_set
245    }
246
247    // PUBLIC MUTATORS
248    // --------------------------------------------------------------------------------------------
249
250    /// Consumes self and returns the underlying [`MutationSet`].
251    pub fn into_mutation_set(self) -> MutationSet<SMT_DEPTH, Word, Word> {
252        self.mutation_set
253    }
254}
255
256// NULLIFIER BLOCK
257// ================================================================================================
258
259/// The [`BlockNumber`] at which a [`Nullifier`] was consumed.
260///
261/// Since there are no nullifiers in the genesis block the [`BlockNumber::GENESIS`] is used to
262/// signal an unconsumed nullifier.
263///
264/// This type can be converted to a [`Word`] which is laid out like this:
265///
266/// ```text
267/// [block_num, 0, 0, 0]
268/// ```
269#[derive(Debug, PartialEq, Eq, Copy, Clone)]
270pub struct NullifierBlock(BlockNumber);
271
272impl NullifierBlock {
273    pub const UNSPENT: NullifierBlock = NullifierBlock(BlockNumber::GENESIS);
274
275    /// Returns a new [NullifierBlock] constructed from the provided word.
276    ///
277    /// # Errors
278    /// Returns an error if:
279    /// - The 0th element in the word is not a valid [BlockNumber].
280    /// - Any of the remaining elements is non-zero.
281    pub fn new(word: Word) -> Result<Self, NullifierTreeError> {
282        let block_num = u32::try_from(word[0].as_canonical_u64())
283            .map(BlockNumber::from)
284            .map_err(|_| NullifierTreeError::InvalidNullifierBlockNumber(word))?;
285
286        if word[1..4].iter().any(|felt| *felt != Felt::ZERO) {
287            return Err(NullifierTreeError::InvalidNullifierBlockNumber(word));
288        }
289
290        Ok(NullifierBlock(block_num))
291    }
292
293    /// Returns true if the nullifier has already been spent.
294    pub fn is_spent(&self) -> bool {
295        !self.is_unspent()
296    }
297
298    /// Returns true if the nullifier has not yet been spent.
299    pub fn is_unspent(&self) -> bool {
300        self == &Self::UNSPENT
301    }
302}
303
304impl From<BlockNumber> for NullifierBlock {
305    fn from(block_num: BlockNumber) -> Self {
306        Self(block_num)
307    }
308}
309
310impl From<NullifierBlock> for BlockNumber {
311    fn from(value: NullifierBlock) -> BlockNumber {
312        value.0
313    }
314}
315
316impl From<NullifierBlock> for Word {
317    fn from(value: NullifierBlock) -> Word {
318        Word::from([Felt::from(value.0), Felt::ZERO, Felt::ZERO, Felt::ZERO])
319    }
320}
321
322impl TryFrom<Word> for NullifierBlock {
323    type Error = NullifierTreeError;
324
325    fn try_from(value: Word) -> Result<Self, Self::Error> {
326        Self::new(value)
327    }
328}
329
330// TESTS
331// ================================================================================================
332
333#[cfg(test)]
334mod tests {
335    use assert_matches::assert_matches;
336
337    use super::NullifierTree;
338    use crate::Word;
339    use crate::block::BlockNumber;
340    use crate::block::nullifier_tree::NullifierBlock;
341    use crate::errors::NullifierTreeError;
342    use crate::note::Nullifier;
343
344    #[test]
345    fn leaf_value_encode_decode() {
346        let block_num = BlockNumber::from(0xffff_ffff_u32);
347        let nullifier_block = NullifierBlock::from(block_num);
348        let block_num_recovered = nullifier_block.into();
349        assert_eq!(block_num, block_num_recovered);
350    }
351
352    #[test]
353    fn leaf_value_encoding() {
354        let block_num = BlockNumber::from(123);
355        let nullifier_value = NullifierBlock::from(block_num);
356        assert_eq!(
357            nullifier_value,
358            NullifierBlock::new(Word::from([block_num.as_u32(), 0, 0, 0u32])).unwrap()
359        );
360    }
361
362    #[test]
363    fn leaf_value_decoding() {
364        let block_num = 123;
365        let nullifier_value = NullifierBlock::new(Word::from([block_num, 0, 0, 0u32])).unwrap();
366        let decoded_block_num: BlockNumber = nullifier_value.into();
367
368        assert_eq!(decoded_block_num, block_num.into());
369    }
370
371    #[test]
372    fn apply_mutations() {
373        let nullifier1 = Nullifier::dummy(1);
374        let nullifier2 = Nullifier::dummy(2);
375        let nullifier3 = Nullifier::dummy(3);
376
377        let block1 = BlockNumber::from(1);
378        let block2 = BlockNumber::from(2);
379        let block3 = BlockNumber::from(3);
380
381        let mut tree = NullifierTree::with_entries([(nullifier1, block1)]).unwrap();
382
383        // Check that passing nullifier2 twice with different values will use the last value.
384        let mutations = tree
385            .compute_mutations([(nullifier2, block1), (nullifier3, block3), (nullifier2, block2)])
386            .unwrap();
387
388        tree.apply_mutations(mutations).unwrap();
389
390        assert_eq!(tree.num_nullifiers(), 3);
391        assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1);
392        assert_eq!(tree.get_block_num(&nullifier2).unwrap(), block2);
393        assert_eq!(tree.get_block_num(&nullifier3).unwrap(), block3);
394    }
395
396    #[test]
397    fn nullifier_already_spent() {
398        let nullifier1 = Nullifier::dummy(1);
399
400        let block1 = BlockNumber::from(1);
401        let block2 = BlockNumber::from(2);
402
403        let mut tree = NullifierTree::with_entries([(nullifier1, block1)]).unwrap();
404
405        // Attempt to insert nullifier 1 again at _the same_ block number.
406        let err = tree.clone().compute_mutations([(nullifier1, block1)]).unwrap_err();
407        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
408
409        let err = tree.clone().mark_spent(nullifier1, block1).unwrap_err();
410        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
411
412        // Attempt to insert nullifier 1 again at a different block number.
413        let err = tree.clone().compute_mutations([(nullifier1, block2)]).unwrap_err();
414        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
415
416        let err = tree.mark_spent(nullifier1, block2).unwrap_err();
417        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
418    }
419
420    #[cfg(feature = "std")]
421    #[test]
422    fn large_smt_backend_basic_operations() {
423        use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
424
425        // Create test data
426        let nullifier1 = Nullifier::dummy(1);
427        let nullifier2 = Nullifier::dummy(2);
428        let nullifier3 = Nullifier::dummy(3);
429
430        let block1 = BlockNumber::from(1);
431        let block2 = BlockNumber::from(2);
432        let block3 = BlockNumber::from(3);
433
434        // Create NullifierTree with LargeSmt backend
435        let mut tree = NullifierTree::new_unchecked(
436            LargeSmt::with_entries(
437                MemoryStorage::default(),
438                [
439                    (nullifier1.as_word(), NullifierBlock::from(block1).into()),
440                    (nullifier2.as_word(), NullifierBlock::from(block2).into()),
441                ],
442            )
443            .unwrap(),
444        );
445
446        // Test basic operations
447        assert_eq!(tree.num_nullifiers(), 2);
448        assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1);
449        assert_eq!(tree.get_block_num(&nullifier2).unwrap(), block2);
450
451        // Test opening
452        let _witness1 = tree.open(&nullifier1);
453
454        // Test mutations
455        tree.mark_spent(nullifier3, block3).unwrap();
456        assert_eq!(tree.num_nullifiers(), 3);
457        assert_eq!(tree.get_block_num(&nullifier3).unwrap(), block3);
458    }
459
460    #[cfg(feature = "std")]
461    #[test]
462    fn large_smt_backend_nullifier_already_spent() {
463        use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
464
465        let nullifier1 = Nullifier::dummy(1);
466
467        let block1 = BlockNumber::from(1);
468        let block2 = BlockNumber::from(2);
469
470        let mut tree = NullifierTree::new_unchecked(
471            LargeSmt::with_entries(
472                MemoryStorage::default(),
473                [(nullifier1.as_word(), NullifierBlock::from(block1).into())],
474            )
475            .unwrap(),
476        );
477
478        assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1);
479
480        let err = tree.mark_spent(nullifier1, block2).unwrap_err();
481        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
482    }
483
484    #[cfg(feature = "std")]
485    #[test]
486    fn large_smt_backend_apply_mutations() {
487        use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
488
489        let nullifier1 = Nullifier::dummy(1);
490        let nullifier2 = Nullifier::dummy(2);
491        let nullifier3 = Nullifier::dummy(3);
492
493        let block1 = BlockNumber::from(1);
494        let block2 = BlockNumber::from(2);
495        let block3 = BlockNumber::from(3);
496
497        let mut tree = LargeSmt::with_entries(
498            MemoryStorage::default(),
499            [(nullifier1.as_word(), NullifierBlock::from(block1).into())],
500        )
501        .map(NullifierTree::new_unchecked)
502        .unwrap();
503
504        let mutations =
505            tree.compute_mutations([(nullifier2, block2), (nullifier3, block3)]).unwrap();
506
507        tree.apply_mutations(mutations).unwrap();
508
509        assert_eq!(tree.num_nullifiers(), 3);
510        assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1);
511        assert_eq!(tree.get_block_num(&nullifier2).unwrap(), block2);
512        assert_eq!(tree.get_block_num(&nullifier3).unwrap(), block3);
513    }
514
515    #[cfg(feature = "std")]
516    #[test]
517    fn large_smt_backend_same_root_as_regular_smt() {
518        use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
519
520        let nullifier1 = Nullifier::dummy(1);
521        let nullifier2 = Nullifier::dummy(2);
522
523        let block1 = BlockNumber::from(1);
524        let block2 = BlockNumber::from(2);
525
526        // Create tree with LargeSmt backend
527        let large_tree = LargeSmt::with_entries(
528            MemoryStorage::default(),
529            [
530                (nullifier1.as_word(), NullifierBlock::from(block1).into()),
531                (nullifier2.as_word(), NullifierBlock::from(block2).into()),
532            ],
533        )
534        .map(NullifierTree::new_unchecked)
535        .unwrap();
536
537        // Create tree with regular Smt backend
538        let regular_tree =
539            NullifierTree::with_entries([(nullifier1, block1), (nullifier2, block2)]).unwrap();
540
541        // Both should have the same root
542        assert_eq!(large_tree.root(), regular_tree.root());
543
544        // Both should have the same nullifier entries
545        let large_entries: std::collections::BTreeMap<_, _> = large_tree.entries().collect();
546        let regular_entries: std::collections::BTreeMap<_, _> = regular_tree.entries().collect();
547
548        assert_eq!(large_entries, regular_entries);
549    }
550}