miden_objects/note/
nullifier.rs

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