miden_objects/note/
note_id.rs

1use alloc::string::String;
2use core::fmt::Display;
3
4use super::{Digest, Felt, Hasher, NoteDetails, Word};
5use crate::utils::{
6    serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7    HexParseError,
8};
9
10// NOTE ID
11// ================================================================================================
12
13/// Returns a unique identifier of a note, which is simultaneously a commitment to the note.
14///
15/// Note ID is computed as:
16///
17/// > hash(recipient, asset_hash),
18///
19/// where `recipient` is defined as:
20///
21/// > hash(hash(hash(serial_num, ZERO), script_hash), input_hash)
22///
23/// This achieves the following properties:
24/// - Every note can be reduced to a single unique ID.
25/// - To compute a note ID, we do not need to know the note's serial_num. Knowing the hash of the
26///   serial_num (as well as script hash, input hash, and note assets) is sufficient.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
28pub struct NoteId(Digest);
29
30impl NoteId {
31    /// Returns a new [NoteId] instantiated from the provided note components.
32    pub fn new(recipient: Digest, asset_commitment: Digest) -> Self {
33        Self(Hasher::merge(&[recipient, asset_commitment]))
34    }
35
36    /// Returns the elements representation of this note ID.
37    pub fn as_elements(&self) -> &[Felt] {
38        self.0.as_elements()
39    }
40
41    /// Returns the byte representation of this note ID.
42    pub fn as_bytes(&self) -> [u8; 32] {
43        self.0.as_bytes()
44    }
45
46    /// Returns a big-endian, hex-encoded string.
47    pub fn to_hex(&self) -> String {
48        self.0.to_hex()
49    }
50
51    /// Returns the digest defining this note ID.
52    pub fn inner(&self) -> Digest {
53        self.0
54    }
55}
56
57impl Display for NoteId {
58    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
59        write!(f, "{}", self.to_hex())
60    }
61}
62
63// CONVERSIONS INTO NOTE ID
64// ================================================================================================
65
66impl From<&NoteDetails> for NoteId {
67    fn from(note: &NoteDetails) -> Self {
68        Self::new(note.recipient().digest(), note.assets().commitment())
69    }
70}
71
72impl From<Word> for NoteId {
73    fn from(value: Word) -> Self {
74        Self(value.into())
75    }
76}
77
78impl From<Digest> for NoteId {
79    fn from(value: Digest) -> Self {
80        Self(value)
81    }
82}
83
84impl NoteId {
85    /// Attempts to convert from a hexadecimal string to [NoteId].
86    pub fn try_from_hex(hex_value: &str) -> Result<NoteId, HexParseError> {
87        Digest::try_from(hex_value).map(NoteId::from)
88    }
89}
90
91// CONVERSIONS FROM NOTE ID
92// ================================================================================================
93
94impl From<NoteId> for Digest {
95    fn from(id: NoteId) -> Self {
96        id.inner()
97    }
98}
99
100impl From<NoteId> for Word {
101    fn from(id: NoteId) -> Self {
102        id.0.into()
103    }
104}
105
106impl From<NoteId> for [u8; 32] {
107    fn from(id: NoteId) -> Self {
108        id.0.into()
109    }
110}
111
112impl From<&NoteId> for Word {
113    fn from(id: &NoteId) -> Self {
114        id.0.into()
115    }
116}
117
118impl From<&NoteId> for [u8; 32] {
119    fn from(id: &NoteId) -> Self {
120        id.0.into()
121    }
122}
123
124// SERIALIZATION
125// ================================================================================================
126
127impl Serializable for NoteId {
128    fn write_into<W: ByteWriter>(&self, target: &mut W) {
129        target.write_bytes(&self.0.to_bytes());
130    }
131}
132
133impl Deserializable for NoteId {
134    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
135        let id = Digest::read_from(source)?;
136        Ok(Self(id))
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use alloc::string::ToString;
143
144    use super::NoteId;
145
146    #[test]
147    fn note_id_try_from_hex() {
148        let note_id_hex = "0xc9d31c82c098e060c9b6e3af2710b3fc5009a1a6f82ef9465f8f35d1f5ba4a80";
149        let note_id = NoteId::try_from_hex(note_id_hex).unwrap();
150
151        assert_eq!(note_id.inner().to_string(), note_id_hex)
152    }
153}