miden-protocol 0.15.0

Core components of the Miden protocol
Documentation
use miden_crypto::Word;

use crate::account::AccountId;
use crate::errors::NoteError;
use crate::utils::serde::{
    ByteReader,
    ByteWriter,
    Deserializable,
    DeserializationError,
    Serializable,
};
use crate::{Felt, Hasher, ZERO};

mod assets;
pub use assets::NoteAssets;

mod details;
pub use details::NoteDetails;

mod header;
pub use header::NoteHeader;

mod storage;
pub use storage::NoteStorage;

mod metadata;
pub use metadata::{NoteMetadata, PartialNoteMetadata};

mod attachment;
pub use attachment::{
    NoteAttachment,
    NoteAttachmentContent,
    NoteAttachmentHeader,
    NoteAttachmentScheme,
    NoteAttachments,
};

mod note_id;
pub use note_id::NoteId;

mod note_details_commitment;
pub use note_details_commitment::NoteDetailsCommitment;

mod note_tag;
pub use note_tag::NoteTag;

mod note_type;
pub use note_type::NoteType;

mod nullifier;
pub use nullifier::Nullifier;

mod location;
pub use location::{NoteInclusionProof, NoteLocation};

mod partial;
pub use partial::PartialNote;

mod recipient;
pub use recipient::NoteRecipient;

mod script;
pub use script::{NoteScript, NoteScriptRoot};

mod file;
pub use file::NoteFile;

// NOTE
// ================================================================================================

/// A note with all the data required for it to be consumed by executing it against the transaction
/// kernel.
///
/// Notes consist of note metadata, attachments and details. Note metadata and attachments are
/// always public, but details are either private or public, depending on the note type. Note
/// details consist of note assets, script, storage, and a serial number, the three latter grouped
/// into a recipient object.
///
/// Note details can be reduced to a [NoteDetailsCommitment]. Together with the note metadata,
/// this commitment determines the public [NoteId]. Full note details and metadata can also be
/// reduced to a [Nullifier], which is known only to entities which have access to full note data.
///
/// Fungible and non-fungible asset transfers are done by moving assets to the note's assets. The
/// note's script determines the conditions required for the note consumption, i.e. the target
/// account of a P2ID or conditions of a SWAP, and the effects of the note. The serial number has
/// a double duty of preventing double spend, and providing unlikability to the consumer of a note.
/// The note's storage allows for customization of its script.
///
/// To create a note, the kernel does not require all the information above, a user can create a
/// note only with the commitment to the script, storage, the serial number (i.e., the recipient),
/// and the kernel only verifies the source account has the assets necessary for the note creation.
/// See [NoteRecipient] for more details.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Note {
    header: NoteHeader,
    details: NoteDetails,
    attachments: NoteAttachments,

    nullifier: Nullifier,
}

impl Note {
    // CONSTRUCTOR
    // --------------------------------------------------------------------------------------------

    /// Returns a new [Note] created with the specified parameters and empty attachments.
    pub fn new(
        assets: NoteAssets,
        partial_metadata: PartialNoteMetadata,
        recipient: NoteRecipient,
    ) -> Self {
        Self::with_attachments(assets, partial_metadata, recipient, NoteAttachments::default())
    }

    /// Returns a new [Note] created with the specified parameters and attachments.
    pub fn with_attachments(
        assets: NoteAssets,
        partial_metadata: PartialNoteMetadata,
        recipient: NoteRecipient,
        attachments: NoteAttachments,
    ) -> Self {
        let details = NoteDetails::new(assets, recipient);
        let metadata = NoteMetadata::new(partial_metadata, &attachments);
        let header = NoteHeader::new(details.commitment(), metadata);
        let nullifier = Nullifier::from_details_and_metadata(&details, &metadata);

        Self { header, details, attachments, nullifier }
    }

    // PUBLIC ACCESSORS
    // --------------------------------------------------------------------------------------------

    /// Returns the note's header.
    pub fn header(&self) -> &NoteHeader {
        &self.header
    }

    /// Returns the note's unique identifier.
    ///
    /// This value commits to the note details and metadata.
    pub fn id(&self) -> NoteId {
        self.header.id()
    }

    /// Returns the commitment to the note's details, excluding metadata.
    pub fn details_commitment(&self) -> NoteDetailsCommitment {
        self.header.details_commitment()
    }

    /// Returns the note's assets.
    pub fn assets(&self) -> &NoteAssets {
        self.details.assets()
    }

    /// Returns the note's recipient serial_num, the secret required to consume the note.
    pub fn serial_num(&self) -> Word {
        self.details.serial_num()
    }

    /// Returns the note's recipient script which locks the assets of this note.
    pub fn script(&self) -> &NoteScript {
        self.details.script()
    }

    /// Returns the note's recipient storage which customizes the script's behavior.
    pub fn storage(&self) -> &NoteStorage {
        self.details.storage()
    }

    /// Returns the note's recipient.
    pub fn recipient(&self) -> &NoteRecipient {
        self.details.recipient()
    }

    /// Returns the note's nullifier.
    ///
    /// This is public data, used to prevent double spend.
    pub fn nullifier(&self) -> Nullifier {
        self.nullifier
    }

    /// Returns the note's attachments.
    pub fn attachments(&self) -> &NoteAttachments {
        &self.attachments
    }

    /// Returns a reference to the note's metadata.
    pub fn metadata(&self) -> &NoteMetadata {
        self.header.metadata()
    }

    // MUTATORS
    // --------------------------------------------------------------------------------------------

    /// Reduces the size of the note script by stripping all debug info from it.
    pub fn minify_script(&mut self) {
        self.details.minify_script();
    }

    /// Consumes self and returns the underlying parts of the [`Note`].
    pub fn into_parts(self) -> (NoteAssets, NoteMetadata, NoteRecipient, NoteAttachments) {
        let (assets, recipient) = self.details.into_parts();
        let metadata = self.header.into_metadata();
        (assets, metadata, recipient, self.attachments)
    }
}

// AS REF
// ================================================================================================

impl AsRef<NoteRecipient> for Note {
    fn as_ref(&self) -> &NoteRecipient {
        self.recipient()
    }
}

// CONVERSIONS FROM NOTE
// ================================================================================================

impl From<Note> for NoteHeader {
    fn from(note: Note) -> Self {
        note.header
    }
}

impl From<&Note> for NoteDetails {
    fn from(note: &Note) -> Self {
        note.details.clone()
    }
}

impl From<Note> for NoteDetails {
    fn from(note: Note) -> Self {
        note.details
    }
}

impl From<Note> for PartialNote {
    fn from(note: Note) -> Self {
        let (assets, recipient, ..) = note.details.into_parts();
        PartialNote::new(
            note.header.into_metadata().into_partial_metadata(),
            recipient.digest(),
            assets,
            note.attachments,
        )
    }
}

impl From<&Note> for NoteHeader {
    fn from(note: &Note) -> Self {
        note.header
    }
}

// SERIALIZATION
// ================================================================================================

impl Serializable for Note {
    fn write_into<W: ByteWriter>(&self, target: &mut W) {
        let Self {
            header,
            details,
            attachments,

            // nullifier is not serialized as it can be computed from the rest of the data
            nullifier: _,
        } = self;

        // Serialize only partial metadata since note ID can be recomputed from the note details and
        // attachment schemes and commitments can be reconstructed from attachments
        header.metadata().partial_metadata().write_into(target);
        details.write_into(target);
        attachments.write_into(target);
    }

    fn get_size_hint(&self) -> usize {
        self.header.metadata().partial_metadata().get_size_hint()
            + self.details.get_size_hint()
            + self.attachments.get_size_hint()
    }
}

impl Deserializable for Note {
    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
        let partial_metadata = PartialNoteMetadata::read_from(source)?;
        let details = NoteDetails::read_from(source)?;
        let attachments = NoteAttachments::read_from(source)?;
        let (assets, recipient) = details.into_parts();

        Ok(Self::with_attachments(assets, partial_metadata, recipient, attachments))
    }
}