miden_objects/note/
note_id.rs

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