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    /// - the list of provided nullifiers contains duplicates.
128    pub fn compute_mutations<I>(
129        &self,
130        nullifiers: impl IntoIterator<Item = (Nullifier, BlockNumber), IntoIter = I>,
131    ) -> Result<NullifierMutationSet, NullifierTreeError>
132    where
133        I: Iterator<Item = (Nullifier, BlockNumber)> + Clone,
134    {
135        let nullifiers = nullifiers.into_iter();
136        for (nullifier, _) in nullifiers.clone() {
137            if self.get_block_num(&nullifier).is_some() {
138                return Err(NullifierTreeError::NullifierAlreadySpent(nullifier));
139            }
140        }
141
142        let mutation_set = self
143            .smt
144            .compute_mutations(
145                nullifiers
146                    .into_iter()
147                    .map(|(nullifier, block_num)| {
148                        (nullifier.as_word(), NullifierBlock::from(block_num).into())
149                    })
150                    .collect::<Vec<_>>(),
151            )
152            .map_err(NullifierTreeError::ComputeMutations)?;
153
154        Ok(NullifierMutationSet::new(mutation_set))
155    }
156
157    // PUBLIC MUTATORS
158    // --------------------------------------------------------------------------------------------
159
160    /// Marks the given nullifier as spent at the given block number.
161    ///
162    /// # Errors
163    ///
164    /// Returns an error if:
165    /// - the nullifier was already spent.
166    pub fn mark_spent(
167        &mut self,
168        nullifier: Nullifier,
169        block_num: BlockNumber,
170    ) -> Result<(), NullifierTreeError> {
171        let prev_nullifier_value = self
172            .smt
173            .insert(nullifier.as_word(), NullifierBlock::from(block_num))
174            .map_err(NullifierTreeError::MaxLeafEntriesExceeded)?;
175
176        if prev_nullifier_value.is_spent() {
177            Err(NullifierTreeError::NullifierAlreadySpent(nullifier))
178        } else {
179            Ok(())
180        }
181    }
182
183    /// Applies mutations to the nullifier tree.
184    ///
185    /// # Errors
186    ///
187    /// Returns an error if:
188    /// - `mutations` was computed on a tree with a different root than this one.
189    pub fn apply_mutations(
190        &mut self,
191        mutations: NullifierMutationSet,
192    ) -> Result<(), NullifierTreeError> {
193        self.smt
194            .apply_mutations(mutations.into_mutation_set())
195            .map_err(NullifierTreeError::TreeRootConflict)
196    }
197}
198
199// SERIALIZATION
200// ================================================================================================
201
202impl Serializable for NullifierTree {
203    fn write_into<W: ByteWriter>(&self, target: &mut W) {
204        self.entries().collect::<Vec<_>>().write_into(target);
205    }
206}
207
208impl Deserializable for NullifierTree {
209    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
210        let entries = Vec::<(Nullifier, BlockNumber)>::read_from(source)?;
211        Self::with_entries(entries)
212            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
213    }
214}
215
216// NULLIFIER MUTATION SET
217// ================================================================================================
218
219/// A newtype wrapper around a [`MutationSet`] for use in the [`NullifierTree`].
220///
221/// It guarantees that applying the contained mutations will result in a nullifier tree where
222/// nullifier's block numbers are not updated (except if they were unspent before), ensuring that
223/// nullifiers are only spent once.
224///
225/// It is returned by and used in methods on the [`NullifierTree`].
226#[derive(Debug, Clone, PartialEq, Eq)]
227pub struct NullifierMutationSet {
228    mutation_set: MutationSet<SMT_DEPTH, Word, Word>,
229}
230
231impl NullifierMutationSet {
232    // CONSTRUCTORS
233    // --------------------------------------------------------------------------------------------
234
235    /// Creates a new [`NullifierMutationSet`] from the provided raw mutation set.
236    fn new(mutation_set: MutationSet<SMT_DEPTH, Word, Word>) -> Self {
237        Self { mutation_set }
238    }
239
240    // PUBLIC ACCESSORS
241    // --------------------------------------------------------------------------------------------
242
243    /// Returns a reference to the underlying [`MutationSet`].
244    pub fn as_mutation_set(&self) -> &MutationSet<SMT_DEPTH, Word, Word> {
245        &self.mutation_set
246    }
247
248    // PUBLIC MUTATORS
249    // --------------------------------------------------------------------------------------------
250
251    /// Consumes self and returns the underlying [`MutationSet`].
252    pub fn into_mutation_set(self) -> MutationSet<SMT_DEPTH, Word, Word> {
253        self.mutation_set
254    }
255}
256
257// NULLIFIER BLOCK
258// ================================================================================================
259
260/// The [`BlockNumber`] at which a [`Nullifier`] was consumed.
261///
262/// Since there are no nullifiers in the genesis block the [`BlockNumber::GENESIS`] is used to
263/// signal an unconsumed nullifier.
264///
265/// This type can be converted to a [`Word`] which is laid out like this:
266///
267/// ```text
268/// [block_num, 0, 0, 0]
269/// ```
270#[derive(Debug, PartialEq, Eq, Copy, Clone)]
271pub struct NullifierBlock(BlockNumber);
272
273impl NullifierBlock {
274    pub const UNSPENT: NullifierBlock = NullifierBlock(BlockNumber::GENESIS);
275
276    /// Returns a new [NullifierBlock] constructed from the provided word.
277    ///
278    /// # Errors
279    /// Returns an error if:
280    /// - The 0th element in the word is not a valid [BlockNumber].
281    /// - Any of the remaining elements is non-zero.
282    pub fn new(word: Word) -> Result<Self, NullifierTreeError> {
283        let block_num = u32::try_from(word[0].as_canonical_u64())
284            .map(BlockNumber::from)
285            .map_err(|_| NullifierTreeError::InvalidNullifierBlockNumber(word))?;
286
287        if word[1..4].iter().any(|felt| *felt != Felt::ZERO) {
288            return Err(NullifierTreeError::InvalidNullifierBlockNumber(word));
289        }
290
291        Ok(NullifierBlock(block_num))
292    }
293
294    /// Returns true if the nullifier has already been spent.
295    pub fn is_spent(&self) -> bool {
296        !self.is_unspent()
297    }
298
299    /// Returns true if the nullifier has not yet been spent.
300    pub fn is_unspent(&self) -> bool {
301        self == &Self::UNSPENT
302    }
303}
304
305impl From<BlockNumber> for NullifierBlock {
306    fn from(block_num: BlockNumber) -> Self {
307        Self(block_num)
308    }
309}
310
311impl From<NullifierBlock> for BlockNumber {
312    fn from(value: NullifierBlock) -> BlockNumber {
313        value.0
314    }
315}
316
317impl From<NullifierBlock> for Word {
318    fn from(value: NullifierBlock) -> Word {
319        Word::from([Felt::from(value.0), Felt::ZERO, Felt::ZERO, Felt::ZERO])
320    }
321}
322
323impl TryFrom<Word> for NullifierBlock {
324    type Error = NullifierTreeError;
325
326    fn try_from(value: Word) -> Result<Self, Self::Error> {
327        Self::new(value)
328    }
329}
330
331// TESTS
332// ================================================================================================
333
334#[cfg(test)]
335mod tests {
336    use assert_matches::assert_matches;
337
338    use super::NullifierTree;
339    use crate::Word;
340    use crate::block::BlockNumber;
341    use crate::block::nullifier_tree::NullifierBlock;
342    use crate::errors::NullifierTreeError;
343    use crate::note::Nullifier;
344
345    #[test]
346    fn leaf_value_encode_decode() {
347        let block_num = BlockNumber::from(0xffff_ffff_u32);
348        let nullifier_block = NullifierBlock::from(block_num);
349        let block_num_recovered = nullifier_block.into();
350        assert_eq!(block_num, block_num_recovered);
351    }
352
353    #[test]
354    fn leaf_value_encoding() {
355        let block_num = BlockNumber::from(123);
356        let nullifier_value = NullifierBlock::from(block_num);
357        assert_eq!(
358            nullifier_value,
359            NullifierBlock::new(Word::from([block_num.as_u32(), 0, 0, 0u32])).unwrap()
360        );
361    }
362
363    #[test]
364    fn leaf_value_decoding() {
365        let block_num = 123;
366        let nullifier_value = NullifierBlock::new(Word::from([block_num, 0, 0, 0u32])).unwrap();
367        let decoded_block_num: BlockNumber = nullifier_value.into();
368
369        assert_eq!(decoded_block_num, block_num.into());
370    }
371
372    #[test]
373    fn apply_mutations() {
374        let nullifier1 = Nullifier::dummy(1);
375        let nullifier2 = Nullifier::dummy(2);
376        let nullifier3 = Nullifier::dummy(3);
377
378        let block1 = BlockNumber::from(1);
379        let block2 = BlockNumber::from(2);
380        let block3 = BlockNumber::from(3);
381
382        let mut tree = NullifierTree::with_entries([(nullifier1, block1)]).unwrap();
383
384        let mutations =
385            tree.compute_mutations([(nullifier2, block2), (nullifier3, block3)]).unwrap();
386
387        tree.apply_mutations(mutations).unwrap();
388
389        assert_eq!(tree.num_nullifiers(), 3);
390        assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1);
391        assert_eq!(tree.get_block_num(&nullifier2).unwrap(), block2);
392        assert_eq!(tree.get_block_num(&nullifier3).unwrap(), block3);
393    }
394
395    #[test]
396    fn nullifier_already_spent() {
397        let nullifier1 = Nullifier::dummy(1);
398
399        let block1 = BlockNumber::from(1);
400        let block2 = BlockNumber::from(2);
401
402        let mut tree = NullifierTree::with_entries([(nullifier1, block1)]).unwrap();
403
404        // Attempt to insert nullifier 1 again at _the same_ block number.
405        let err = tree.clone().compute_mutations([(nullifier1, block1)]).unwrap_err();
406        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
407
408        let err = tree.clone().mark_spent(nullifier1, block1).unwrap_err();
409        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
410
411        // Attempt to insert nullifier 1 again at a different block number.
412        let err = tree.clone().compute_mutations([(nullifier1, block2)]).unwrap_err();
413        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
414
415        let err = tree.mark_spent(nullifier1, block2).unwrap_err();
416        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
417    }
418
419    #[cfg(feature = "std")]
420    #[test]
421    fn large_smt_backend_basic_operations() {
422        use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
423
424        // Create test data
425        let nullifier1 = Nullifier::dummy(1);
426        let nullifier2 = Nullifier::dummy(2);
427        let nullifier3 = Nullifier::dummy(3);
428
429        let block1 = BlockNumber::from(1);
430        let block2 = BlockNumber::from(2);
431        let block3 = BlockNumber::from(3);
432
433        // Create NullifierTree with LargeSmt backend
434        let mut tree = NullifierTree::new_unchecked(
435            LargeSmt::with_entries(
436                MemoryStorage::default(),
437                [
438                    (nullifier1.as_word(), NullifierBlock::from(block1).into()),
439                    (nullifier2.as_word(), NullifierBlock::from(block2).into()),
440                ],
441            )
442            .unwrap(),
443        );
444
445        // Test basic operations
446        assert_eq!(tree.num_nullifiers(), 2);
447        assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1);
448        assert_eq!(tree.get_block_num(&nullifier2).unwrap(), block2);
449
450        // Test opening
451        let _witness1 = tree.open(&nullifier1);
452
453        // Test mutations
454        tree.mark_spent(nullifier3, block3).unwrap();
455        assert_eq!(tree.num_nullifiers(), 3);
456        assert_eq!(tree.get_block_num(&nullifier3).unwrap(), block3);
457    }
458
459    #[cfg(feature = "std")]
460    #[test]
461    fn large_smt_backend_nullifier_already_spent() {
462        use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
463
464        let nullifier1 = Nullifier::dummy(1);
465
466        let block1 = BlockNumber::from(1);
467        let block2 = BlockNumber::from(2);
468
469        let mut tree = NullifierTree::new_unchecked(
470            LargeSmt::with_entries(
471                MemoryStorage::default(),
472                [(nullifier1.as_word(), NullifierBlock::from(block1).into())],
473            )
474            .unwrap(),
475        );
476
477        assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1);
478
479        let err = tree.mark_spent(nullifier1, block2).unwrap_err();
480        assert_matches!(err, NullifierTreeError::NullifierAlreadySpent(nullifier) if nullifier == nullifier1);
481    }
482
483    #[cfg(feature = "std")]
484    #[test]
485    fn large_smt_backend_apply_mutations() {
486        use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
487
488        let nullifier1 = Nullifier::dummy(1);
489        let nullifier2 = Nullifier::dummy(2);
490        let nullifier3 = Nullifier::dummy(3);
491
492        let block1 = BlockNumber::from(1);
493        let block2 = BlockNumber::from(2);
494        let block3 = BlockNumber::from(3);
495
496        let mut tree = LargeSmt::with_entries(
497            MemoryStorage::default(),
498            [(nullifier1.as_word(), NullifierBlock::from(block1).into())],
499        )
500        .map(NullifierTree::new_unchecked)
501        .unwrap();
502
503        let mutations =
504            tree.compute_mutations([(nullifier2, block2), (nullifier3, block3)]).unwrap();
505
506        tree.apply_mutations(mutations).unwrap();
507
508        assert_eq!(tree.num_nullifiers(), 3);
509        assert_eq!(tree.get_block_num(&nullifier1).unwrap(), block1);
510        assert_eq!(tree.get_block_num(&nullifier2).unwrap(), block2);
511        assert_eq!(tree.get_block_num(&nullifier3).unwrap(), block3);
512    }
513
514    #[cfg(feature = "std")]
515    #[test]
516    fn large_smt_backend_same_root_as_regular_smt() {
517        use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
518
519        let nullifier1 = Nullifier::dummy(1);
520        let nullifier2 = Nullifier::dummy(2);
521
522        let block1 = BlockNumber::from(1);
523        let block2 = BlockNumber::from(2);
524
525        // Create tree with LargeSmt backend
526        let large_tree = LargeSmt::with_entries(
527            MemoryStorage::default(),
528            [
529                (nullifier1.as_word(), NullifierBlock::from(block1).into()),
530                (nullifier2.as_word(), NullifierBlock::from(block2).into()),
531            ],
532        )
533        .map(NullifierTree::new_unchecked)
534        .unwrap();
535
536        // Create tree with regular Smt backend
537        let regular_tree =
538            NullifierTree::with_entries([(nullifier1, block1), (nullifier2, block2)]).unwrap();
539
540        // Both should have the same root
541        assert_eq!(large_tree.root(), regular_tree.root());
542
543        // Both should have the same nullifier entries
544        let large_entries: std::collections::BTreeMap<_, _> = large_tree.entries().collect();
545        let regular_entries: std::collections::BTreeMap<_, _> = regular_tree.entries().collect();
546
547        assert_eq!(large_entries, regular_entries);
548    }
549}