Skip to main content

miden_protocol/note/
nullifier.rs

1use alloc::string::String;
2use core::fmt::{Debug, Display, Formatter};
3
4use miden_core::WORD_SIZE;
5use miden_crypto::WordError;
6use miden_protocol_macros::WordWrapper;
7
8use super::{
9    ByteReader,
10    ByteWriter,
11    Deserializable,
12    DeserializationError,
13    Felt,
14    Hasher,
15    NoteDetails,
16    Serializable,
17    Word,
18    ZERO,
19};
20
21// CONSTANTS
22// ================================================================================================
23
24const NULLIFIER_PREFIX_SHIFT: u8 = 48;
25
26// NULLIFIER
27// ================================================================================================
28
29/// A note's nullifier.
30///
31/// A note's nullifier is computed as:
32///
33/// > hash(serial_num, script_root, storage_commitment, asset_commitment).
34///
35/// This achieves the following properties:
36/// - Every note can be reduced to a single unique nullifier.
37/// - We cannot derive a note's commitment from its nullifier, or a note's nullifier from its hash.
38/// - To compute the nullifier we must know all components of the note: serial_num, script_root,
39///   storage_commitment and asset_commitment.
40#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, WordWrapper)]
41pub struct Nullifier(Word);
42
43impl Nullifier {
44    /// Returns a new note [Nullifier] instantiated from the provided digest.
45    pub fn new(
46        script_root: Word,
47        storage_commitment: Word,
48        asset_commitment: Word,
49        serial_num: Word,
50    ) -> Self {
51        let mut elements = [ZERO; 4 * WORD_SIZE];
52        elements[..4].copy_from_slice(serial_num.as_elements());
53        elements[4..8].copy_from_slice(script_root.as_elements());
54        elements[8..12].copy_from_slice(storage_commitment.as_elements());
55        elements[12..].copy_from_slice(asset_commitment.as_elements());
56        Self(Hasher::hash_elements(&elements))
57    }
58
59    /// Returns the most significant felt (the last element in array)
60    pub fn most_significant_felt(&self) -> Felt {
61        self.as_elements()[3]
62    }
63
64    /// Returns the prefix of this nullifier.
65    ///
66    /// Nullifier prefix is defined as the 16 most significant bits of the nullifier value.
67    pub fn prefix(&self) -> u16 {
68        (self.as_word()[3].as_canonical_u64() >> NULLIFIER_PREFIX_SHIFT) as u16
69    }
70
71    /// Creates a Nullifier from a hex string. Assumes that the string starts with "0x" and
72    /// that the hexadecimal characters are big-endian encoded.
73    ///
74    /// Callers must ensure the provided value is an actual [`Nullifier`].
75    pub fn from_hex(hex_value: &str) -> Result<Self, WordError> {
76        Word::try_from(hex_value).map(Self::from_raw)
77    }
78
79    #[cfg(any(feature = "testing", test))]
80    pub fn dummy(n: u64) -> Self {
81        Self(Word::new([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(n)]))
82    }
83}
84
85impl Display for Nullifier {
86    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
87        f.write_str(&self.to_hex())
88    }
89}
90
91impl Debug for Nullifier {
92    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
93        Display::fmt(self, f)
94    }
95}
96
97// CONVERSIONS INTO NULLIFIER
98// ================================================================================================
99
100impl From<&NoteDetails> for Nullifier {
101    fn from(note: &NoteDetails) -> Self {
102        Self::new(
103            note.script().root(),
104            note.storage().commitment(),
105            note.assets().commitment(),
106            note.serial_num(),
107        )
108    }
109}
110
111// SERIALIZATION
112// ================================================================================================
113
114impl Serializable for Nullifier {
115    fn write_into<W: ByteWriter>(&self, target: &mut W) {
116        target.write_bytes(&self.0.to_bytes());
117    }
118
119    fn get_size_hint(&self) -> usize {
120        Word::SERIALIZED_SIZE
121    }
122}
123
124impl Deserializable for Nullifier {
125    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
126        let nullifier = Word::read_from(source)?;
127        Ok(Self(nullifier))
128    }
129}
130
131// TESTS
132// ================================================================================================
133
134#[cfg(test)]
135mod tests {
136    use crate::note::Nullifier;
137
138    #[test]
139    fn test_from_hex_and_back() {
140        let nullifier_hex = "0x41e7dbbc8ce63ec25cf2d76d76162f16ef8fd1195288171f5e5a3e178222f6d2";
141        let nullifier = Nullifier::from_hex(nullifier_hex).unwrap();
142
143        assert_eq!(nullifier_hex, nullifier.to_hex());
144    }
145}