miden_objects/note/
nullifier.rs

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