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