Skip to main content

miden_protocol/note/
mod.rs

1use miden_crypto::Word;
2
3use crate::account::AccountId;
4use crate::errors::NoteError;
5use crate::utils::serde::{
6    ByteReader,
7    ByteWriter,
8    Deserializable,
9    DeserializationError,
10    Serializable,
11};
12use crate::{Felt, Hasher, ZERO};
13
14mod assets;
15pub use assets::NoteAssets;
16
17mod details;
18pub use details::NoteDetails;
19
20mod header;
21pub use header::NoteHeader;
22
23mod storage;
24pub use storage::NoteStorage;
25
26mod metadata;
27pub use metadata::{NoteMetadata, PartialNoteMetadata};
28
29mod attachment;
30pub use attachment::{
31    NoteAttachment,
32    NoteAttachmentContent,
33    NoteAttachmentHeader,
34    NoteAttachmentScheme,
35    NoteAttachments,
36};
37
38mod note_id;
39pub use note_id::NoteId;
40
41mod note_details_commitment;
42pub use note_details_commitment::NoteDetailsCommitment;
43
44mod note_tag;
45pub use note_tag::NoteTag;
46
47mod note_type;
48pub use note_type::NoteType;
49
50mod nullifier;
51pub use nullifier::Nullifier;
52
53mod location;
54pub use location::{NoteInclusionProof, NoteLocation};
55
56mod partial;
57pub use partial::PartialNote;
58
59mod recipient;
60pub use recipient::NoteRecipient;
61
62mod script;
63pub use script::{NoteScript, NoteScriptRoot};
64
65// NOTE
66// ================================================================================================
67
68/// A note with all the data required for it to be consumed by executing it against the transaction
69/// kernel.
70///
71/// Notes consist of note metadata, attachments and details. Note metadata and attachments are
72/// always public, but details are either private or public, depending on the note type. Note
73/// details consist of note assets, script, storage, and a serial number, the three latter grouped
74/// into a recipient object.
75///
76/// Note details can be reduced to a [NoteDetailsCommitment]. Together with the note metadata,
77/// this commitment determines the public [NoteId]. Full note details and metadata can also be
78/// reduced to a [Nullifier], which is known only to entities which have access to full note data.
79///
80/// Fungible and non-fungible asset transfers are done by moving assets to the note's assets. The
81/// note's script determines the conditions required for the note consumption, i.e. the target
82/// account of a P2ID or conditions of a SWAP, and the effects of the note. The serial number has
83/// a double duty of preventing double spend, and providing unlikability to the consumer of a note.
84/// The note's storage allows for customization of its script.
85///
86/// To create a note, the kernel does not require all the information above, a user can create a
87/// note only with the commitment to the script, storage, the serial number (i.e., the recipient),
88/// and the kernel only verifies the source account has the assets necessary for the note creation.
89/// See [NoteRecipient] for more details.
90#[derive(Clone, Debug, PartialEq, Eq)]
91pub struct Note {
92    header: NoteHeader,
93    details: NoteDetails,
94    attachments: NoteAttachments,
95
96    nullifier: Nullifier,
97}
98
99impl Note {
100    // CONSTRUCTOR
101    // --------------------------------------------------------------------------------------------
102
103    /// Returns a new [Note] created with the specified parameters and empty attachments.
104    pub fn new(
105        assets: NoteAssets,
106        partial_metadata: PartialNoteMetadata,
107        recipient: NoteRecipient,
108    ) -> Self {
109        Self::with_attachments(assets, partial_metadata, recipient, NoteAttachments::default())
110    }
111
112    /// Returns a new [Note] created with the specified parameters and attachments.
113    pub fn with_attachments(
114        assets: NoteAssets,
115        partial_metadata: PartialNoteMetadata,
116        recipient: NoteRecipient,
117        attachments: NoteAttachments,
118    ) -> Self {
119        let details = NoteDetails::new(assets, recipient);
120        let metadata = NoteMetadata::new(partial_metadata, &attachments);
121        let header = NoteHeader::new(details.commitment(), metadata);
122        let nullifier = Nullifier::from_details_and_metadata(&details, &metadata);
123
124        Self { header, details, attachments, nullifier }
125    }
126
127    // PUBLIC ACCESSORS
128    // --------------------------------------------------------------------------------------------
129
130    /// Returns the note's header.
131    pub fn header(&self) -> &NoteHeader {
132        &self.header
133    }
134
135    /// Returns the note's unique identifier.
136    ///
137    /// This value commits to the note details and metadata.
138    pub fn id(&self) -> NoteId {
139        self.header.id()
140    }
141
142    /// Returns the commitment to the note's details, excluding metadata.
143    pub fn details_commitment(&self) -> NoteDetailsCommitment {
144        self.header.details_commitment()
145    }
146
147    /// Returns the note's details.
148    pub fn details(&self) -> &NoteDetails {
149        &self.details
150    }
151
152    /// Returns the note's assets.
153    pub fn assets(&self) -> &NoteAssets {
154        self.details.assets()
155    }
156
157    /// Returns the note's recipient serial_num, the secret required to consume the note.
158    pub fn serial_num(&self) -> Word {
159        self.details.serial_num()
160    }
161
162    /// Returns the note's recipient script which locks the assets of this note.
163    pub fn script(&self) -> &NoteScript {
164        self.details.script()
165    }
166
167    /// Returns the note's recipient storage which customizes the script's behavior.
168    pub fn storage(&self) -> &NoteStorage {
169        self.details.storage()
170    }
171
172    /// Returns the note's recipient.
173    pub fn recipient(&self) -> &NoteRecipient {
174        self.details.recipient()
175    }
176
177    /// Returns the note's nullifier.
178    ///
179    /// This is public data, used to prevent double spend.
180    pub fn nullifier(&self) -> Nullifier {
181        self.nullifier
182    }
183
184    /// Returns the note's attachments.
185    pub fn attachments(&self) -> &NoteAttachments {
186        &self.attachments
187    }
188
189    /// Returns `true` if the note has at least one attachment.
190    pub fn has_attachments(&self) -> bool {
191        !self.attachments.is_empty()
192    }
193
194    /// Returns a reference to the note's metadata.
195    pub fn metadata(&self) -> &NoteMetadata {
196        self.header.metadata()
197    }
198
199    // MUTATORS
200    // --------------------------------------------------------------------------------------------
201
202    /// Reduces the size of the note script by stripping all debug info from it.
203    pub fn clear_debug_info(&mut self) {
204        self.details.clear_debug_info();
205    }
206
207    /// Consumes self and returns the underlying parts of the [`Note`].
208    pub fn into_parts(self) -> (NoteAssets, NoteMetadata, NoteRecipient, NoteAttachments) {
209        let (assets, recipient) = self.details.into_parts();
210        let metadata = self.header.into_metadata();
211        (assets, metadata, recipient, self.attachments)
212    }
213}
214
215// AS REF
216// ================================================================================================
217
218impl AsRef<NoteRecipient> for Note {
219    fn as_ref(&self) -> &NoteRecipient {
220        self.recipient()
221    }
222}
223
224// CONVERSIONS FROM NOTE
225// ================================================================================================
226
227impl From<Note> for NoteHeader {
228    fn from(note: Note) -> Self {
229        note.header
230    }
231}
232
233impl From<&Note> for NoteDetails {
234    fn from(note: &Note) -> Self {
235        note.details.clone()
236    }
237}
238
239impl From<Note> for NoteDetails {
240    fn from(note: Note) -> Self {
241        note.details
242    }
243}
244
245impl From<Note> for PartialNote {
246    fn from(note: Note) -> Self {
247        let (assets, recipient, ..) = note.details.into_parts();
248        PartialNote::new(
249            note.header.into_metadata().into_partial_metadata(),
250            recipient.digest(),
251            assets,
252            note.attachments,
253        )
254    }
255}
256
257impl From<&Note> for NoteHeader {
258    fn from(note: &Note) -> Self {
259        note.header
260    }
261}
262
263// SERIALIZATION
264// ================================================================================================
265
266impl Serializable for Note {
267    fn write_into<W: ByteWriter>(&self, target: &mut W) {
268        let Self {
269            header,
270            details,
271            attachments,
272
273            // nullifier is not serialized as it can be computed from the rest of the data
274            nullifier: _,
275        } = self;
276
277        // Serialize only partial metadata since note ID can be recomputed from the note details and
278        // attachment schemes and commitments can be reconstructed from attachments
279        header.metadata().partial_metadata().write_into(target);
280        details.write_into(target);
281        attachments.write_into(target);
282    }
283
284    fn get_size_hint(&self) -> usize {
285        self.header.metadata().partial_metadata().get_size_hint()
286            + self.details.get_size_hint()
287            + self.attachments.get_size_hint()
288    }
289}
290
291impl Deserializable for Note {
292    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
293        let partial_metadata = PartialNoteMetadata::read_from(source)?;
294        let details = NoteDetails::read_from(source)?;
295        let attachments = NoteAttachments::read_from(source)?;
296        let (assets, recipient) = details.into_parts();
297
298        Ok(Self::with_attachments(assets, partial_metadata, recipient, attachments))
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use assert_matches::assert_matches;
305
306    use super::*;
307    use crate::utils::serde::{Deserializable, DeserializationError};
308
309    #[test]
310    fn note_deserialization_rejects_unsupported_version() {
311        let error = Note::read_from_bytes(&[0]).unwrap_err();
312
313        assert_matches!(error, DeserializationError::InvalidValue(message) => {
314            assert!(message.contains("note version is 0"));
315        });
316    }
317}