miden_protocol/note/
note_id.rs

1use alloc::string::String;
2use core::fmt::Display;
3
4use miden_protocol_macros::WordWrapper;
5
6use super::{Felt, Hasher, NoteDetails, Word};
7use crate::WordError;
8use crate::utils::serde::{
9    ByteReader,
10    ByteWriter,
11    Deserializable,
12    DeserializationError,
13    Serializable,
14};
15
16// NOTE ID
17// ================================================================================================
18
19/// Returns a unique identifier of a note, which is simultaneously a commitment to the note.
20///
21/// Note ID is computed as:
22///
23/// > hash(recipient, asset_commitment),
24///
25/// where `recipient` is defined as:
26///
27/// > hash(hash(hash(serial_num, ZERO), script_root), input_commitment)
28///
29/// This achieves the following properties:
30/// - Every note can be reduced to a single unique ID.
31/// - To compute a note ID, we do not need to know the note's serial_num. Knowing the hash of the
32///   serial_num (as well as script root, input commitment, and note assets) is sufficient.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, WordWrapper)]
34pub struct NoteId(Word);
35
36impl NoteId {
37    /// Returns a new [NoteId] instantiated from the provided note components.
38    pub fn new(recipient: Word, asset_commitment: Word) -> Self {
39        Self(Hasher::merge(&[recipient, asset_commitment]))
40    }
41}
42
43impl Display for NoteId {
44    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45        write!(f, "{}", self.to_hex())
46    }
47}
48
49// CONVERSIONS INTO NOTE ID
50// ================================================================================================
51
52impl From<&NoteDetails> for NoteId {
53    fn from(note: &NoteDetails) -> Self {
54        Self::new(note.recipient().digest(), note.assets().commitment())
55    }
56}
57
58impl NoteId {
59    /// Attempts to convert from a hexadecimal string to [NoteId].
60    ///
61    /// Callers must ensure the provided value is an actual [`NoteId`].
62    pub fn try_from_hex(hex_value: &str) -> Result<NoteId, WordError> {
63        Word::try_from(hex_value).map(NoteId::from_raw)
64    }
65}
66
67// SERIALIZATION
68// ================================================================================================
69
70impl Serializable for NoteId {
71    fn write_into<W: ByteWriter>(&self, target: &mut W) {
72        target.write_bytes(&self.0.to_bytes());
73    }
74}
75
76impl Deserializable for NoteId {
77    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
78        let id = Word::read_from(source)?;
79        Ok(Self(id))
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use alloc::string::ToString;
86
87    use super::NoteId;
88
89    #[test]
90    fn note_id_try_from_hex() {
91        let note_id_hex = "0xc9d31c82c098e060c9b6e3af2710b3fc5009a1a6f82ef9465f8f35d1f5ba4a80";
92        let note_id = NoteId::try_from_hex(note_id_hex).unwrap();
93
94        assert_eq!(note_id.as_word().to_string(), note_id_hex)
95    }
96}