From 3e8947e252efe7bc1abfa7c609e759de8e722a71 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 3 May 2024 16:21:57 -0700 Subject: [PATCH 1/4] refactor: rename NoteEnvelope into NoteHeader --- miden-tx/src/host/mod.rs | 8 +-- miden-tx/tests/integration/scripts/swap.rs | 7 +-- objects/src/notes/mod.rs | 4 +- .../src/notes/{envelope.rs => note_header.rs} | 58 +++++++++---------- objects/src/transaction/outputs.rs | 10 ++-- objects/src/transaction/proven_tx.rs | 2 +- 6 files changed, 42 insertions(+), 47 deletions(-) rename objects/src/notes/{envelope.rs => note_header.rs} (64%) diff --git a/miden-tx/src/host/mod.rs b/miden-tx/src/host/mod.rs index 6efdeb17b..8a7cef1c9 100644 --- a/miden-tx/src/host/mod.rs +++ b/miden-tx/src/host/mod.rs @@ -8,8 +8,8 @@ use miden_objects::{ accounts::{AccountDelta, AccountId, AccountStorage, AccountStub}, assets::Asset, notes::{ - Note, NoteAssets, NoteEnvelope, NoteId, NoteInputs, NoteMetadata, NoteRecipient, - NoteScript, NoteTag, NoteType, + Note, NoteAssets, NoteHeader, NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript, + NoteTag, NoteType, }, transaction::OutputNote, Digest, Hasher, @@ -145,9 +145,7 @@ impl TransactionHost { OutputNote::Public(Note::new(vault, metadata, recipient)) } else { let note_id = NoteId::new(recipient, vault.commitment()); - OutputNote::Private( - NoteEnvelope::new(note_id, metadata).expect("NoteType checked above"), - ) + OutputNote::Private(NoteHeader::new(note_id, metadata).expect("NoteType checked above")) }; self.output_notes.push(note); diff --git a/miden-tx/tests/integration/scripts/swap.rs b/miden-tx/tests/integration/scripts/swap.rs index b3bbbd846..dd6e679ed 100644 --- a/miden-tx/tests/integration/scripts/swap.rs +++ b/miden-tx/tests/integration/scripts/swap.rs @@ -10,7 +10,7 @@ use miden_objects::{ assembly::ProgramAst, assets::{Asset, AssetVault, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}, crypto::rand::RpoRandomCoin, - notes::{NoteAssets, NoteEnvelope, NoteExecutionHint, NoteId, NoteMetadata, NoteTag, NoteType}, + notes::{NoteAssets, NoteExecutionHint, NoteHeader, NoteId, NoteMetadata, NoteTag, NoteType}, transaction::TransactionArgs, Felt, ZERO, }; @@ -105,8 +105,5 @@ fn prove_swap_script() { let note_id = NoteId::new(recipient, assets.commitment()); let created_note = executed_transaction.output_notes().get_note(0); - assert_eq!( - NoteEnvelope::from(created_note), - NoteEnvelope::new(note_id, note_metadata).unwrap() - ); + assert_eq!(NoteHeader::from(created_note), NoteHeader::new(note_id, note_metadata).unwrap()); } diff --git a/objects/src/notes/mod.rs b/objects/src/notes/mod.rs index 8ff5a0056..f23884057 100644 --- a/objects/src/notes/mod.rs +++ b/objects/src/notes/mod.rs @@ -15,8 +15,8 @@ use crate::{ mod assets; pub use assets::NoteAssets; -mod envelope; -pub use envelope::NoteEnvelope; +mod note_header; +pub use note_header::NoteHeader; mod inputs; pub use inputs::NoteInputs; diff --git a/objects/src/notes/envelope.rs b/objects/src/notes/note_header.rs similarity index 64% rename from objects/src/notes/envelope.rs rename to objects/src/notes/note_header.rs index 8e9467336..9d12e323b 100644 --- a/objects/src/notes/envelope.rs +++ b/objects/src/notes/note_header.rs @@ -6,7 +6,7 @@ use super::{ }; use crate::NoteError; -// NOTE ENVELOPE +// NOTE HEADER // ================================================================================================ /// Holds the strictly required, public information of a note. @@ -14,13 +14,13 @@ use crate::NoteError; /// See [NoteId] and [NoteMetadata] for additional details. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct NoteEnvelope { +pub struct NoteHeader { note_id: NoteId, note_metadata: NoteMetadata, } -impl NoteEnvelope { - /// Returns a new [NoteEnvelope] object. +impl NoteHeader { + /// Returns a new [NoteHeader] object. pub fn new(note_id: NoteId, note_metadata: NoteMetadata) -> Result { let note_type = note_metadata.note_type(); if note_type != NoteType::OffChain { @@ -42,62 +42,62 @@ impl NoteEnvelope { } } -impl From for [Felt; 8] { - fn from(note_envelope: NoteEnvelope) -> Self { - (¬e_envelope).into() +impl From for [Felt; 8] { + fn from(note_header: NoteHeader) -> Self { + (¬e_header).into() } } -impl From for [Word; 2] { - fn from(note_envelope: NoteEnvelope) -> Self { - (¬e_envelope).into() +impl From for [Word; 2] { + fn from(note_header: NoteHeader) -> Self { + (¬e_header).into() } } -impl From for [u8; 64] { - fn from(note_envelope: NoteEnvelope) -> Self { - (¬e_envelope).into() +impl From for [u8; 64] { + fn from(note_header: NoteHeader) -> Self { + (¬e_header).into() } } -impl From<&NoteEnvelope> for [Felt; 8] { - fn from(note_envelope: &NoteEnvelope) -> Self { +impl From<&NoteHeader> for [Felt; 8] { + fn from(note_header: &NoteHeader) -> Self { let mut elements: [Felt; 8] = Default::default(); - elements[..4].copy_from_slice(note_envelope.note_id.as_elements()); - elements[4..].copy_from_slice(&Word::from(note_envelope.metadata())); + elements[..4].copy_from_slice(note_header.note_id.as_elements()); + elements[4..].copy_from_slice(&Word::from(note_header.metadata())); elements } } -impl From<&NoteEnvelope> for [Word; 2] { - fn from(note_envelope: &NoteEnvelope) -> Self { +impl From<&NoteHeader> for [Word; 2] { + fn from(note_header: &NoteHeader) -> Self { let mut elements: [Word; 2] = Default::default(); - elements[0].copy_from_slice(note_envelope.note_id.as_elements()); - elements[1].copy_from_slice(&Word::from(note_envelope.metadata())); + elements[0].copy_from_slice(note_header.note_id.as_elements()); + elements[1].copy_from_slice(&Word::from(note_header.metadata())); elements } } -impl From<&NoteEnvelope> for [u8; 64] { - fn from(note_envelope: &NoteEnvelope) -> Self { +impl From<&NoteHeader> for [u8; 64] { + fn from(note_header: &NoteHeader) -> Self { let mut elements: [u8; 64] = [0; 64]; - let note_metadata_bytes = Word::from(note_envelope.metadata()) + let note_metadata_bytes = Word::from(note_header.metadata()) .iter() .flat_map(|x| x.as_int().to_le_bytes()) .collect::>(); - elements[..32].copy_from_slice(¬e_envelope.note_id.as_bytes()); + elements[..32].copy_from_slice(¬e_header.note_id.as_bytes()); elements[32..].copy_from_slice(¬e_metadata_bytes); elements } } -impl From for NoteEnvelope { +impl From for NoteHeader { fn from(note: Note) -> Self { (¬e).into() } } -impl From<&Note> for NoteEnvelope { +impl From<&Note> for NoteHeader { fn from(note: &Note) -> Self { Self { note_id: note.id(), @@ -109,14 +109,14 @@ impl From<&Note> for NoteEnvelope { // SERIALIZATION // ================================================================================================ -impl Serializable for NoteEnvelope { +impl Serializable for NoteHeader { fn write_into(&self, target: &mut W) { self.note_id.write_into(target); self.note_metadata.write_into(target); } } -impl Deserializable for NoteEnvelope { +impl Deserializable for NoteHeader { fn read_from(source: &mut R) -> Result { let note_id = NoteId::read_from(source)?; let note_metadata = NoteMetadata::read_from(source)?; diff --git a/objects/src/transaction/outputs.rs b/objects/src/transaction/outputs.rs index 2a7cbc5ba..42458e9a9 100644 --- a/objects/src/transaction/outputs.rs +++ b/objects/src/transaction/outputs.rs @@ -6,7 +6,7 @@ use vm_processor::DeserializationError; use crate::{ accounts::AccountStub, - notes::{Note, NoteAssets, NoteEnvelope, NoteId, NoteMetadata}, + notes::{Note, NoteAssets, NoteHeader, NoteId, NoteMetadata}, Digest, Felt, Hasher, TransactionOutputError, Word, MAX_OUTPUT_NOTES_PER_TX, }; @@ -148,7 +148,7 @@ const PRIVATE: u8 = 1; #[derive(Debug, Clone, PartialEq, Eq)] pub enum OutputNote { Public(Note), - Private(NoteEnvelope), + Private(NoteHeader), } impl OutputNote { @@ -192,13 +192,13 @@ impl OutputNote { // CONVERSIONS // ------------------------------------------------------------------------------------------------ -impl From for NoteEnvelope { +impl From for NoteHeader { fn from(value: OutputNote) -> Self { (&value).into() } } -impl From<&OutputNote> for NoteEnvelope { +impl From<&OutputNote> for NoteHeader { fn from(value: &OutputNote) -> Self { match value { OutputNote::Public(note) => note.into(), @@ -229,7 +229,7 @@ impl Deserializable for OutputNote { fn read_from(source: &mut R) -> Result { match source.read_u8()? { PUBLIC => Ok(OutputNote::Public(Note::read_from(source)?)), - PRIVATE => Ok(OutputNote::Private(NoteEnvelope::read_from(source)?)), + PRIVATE => Ok(OutputNote::Private(NoteHeader::read_from(source)?)), v => Err(DeserializationError::InvalidValue(format!("Invalid note type: {v}"))), } } diff --git a/objects/src/transaction/proven_tx.rs b/objects/src/transaction/proven_tx.rs index 80a54cb4f..8067fa223 100644 --- a/objects/src/transaction/proven_tx.rs +++ b/objects/src/transaction/proven_tx.rs @@ -183,7 +183,7 @@ pub struct ProvenTransactionBuilder { /// List of [Nullifier]s of all consumed notes by the transaction. input_notes: Vec, - /// List of [NoteEnvelope]s of all notes created by the transaction. + /// List of [OutputNote]s of all notes created by the transaction. output_notes: Vec, /// Block [Digest] of the transaction's reference block. From 76f4447f3471f4265b0caed03cf0bd55af41e264 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 3 May 2024 16:55:37 -0700 Subject: [PATCH 2/4] refactor: change Note to be based on header and details --- miden-tx/src/host/mod.rs | 2 +- miden-tx/tests/integration/scripts/swap.rs | 2 +- objects/src/notes/details.rs | 115 +++++++++++++++++++++ objects/src/notes/mod.rs | 68 +++++------- objects/src/notes/note_header.rs | 13 +-- objects/src/notes/note_id.rs | 6 +- objects/src/notes/nullifier.rs | 8 +- objects/src/notes/recipient.rs | 2 +- 8 files changed, 157 insertions(+), 59 deletions(-) create mode 100644 objects/src/notes/details.rs diff --git a/miden-tx/src/host/mod.rs b/miden-tx/src/host/mod.rs index 8a7cef1c9..c7dc6f768 100644 --- a/miden-tx/src/host/mod.rs +++ b/miden-tx/src/host/mod.rs @@ -145,7 +145,7 @@ impl TransactionHost { OutputNote::Public(Note::new(vault, metadata, recipient)) } else { let note_id = NoteId::new(recipient, vault.commitment()); - OutputNote::Private(NoteHeader::new(note_id, metadata).expect("NoteType checked above")) + OutputNote::Private(NoteHeader::new(note_id, metadata)) }; self.output_notes.push(note); diff --git a/miden-tx/tests/integration/scripts/swap.rs b/miden-tx/tests/integration/scripts/swap.rs index dd6e679ed..e4e64d117 100644 --- a/miden-tx/tests/integration/scripts/swap.rs +++ b/miden-tx/tests/integration/scripts/swap.rs @@ -105,5 +105,5 @@ fn prove_swap_script() { let note_id = NoteId::new(recipient, assets.commitment()); let created_note = executed_transaction.output_notes().get_note(0); - assert_eq!(NoteHeader::from(created_note), NoteHeader::new(note_id, note_metadata).unwrap()); + assert_eq!(NoteHeader::from(created_note), NoteHeader::new(note_id, note_metadata)); } diff --git a/objects/src/notes/details.rs b/objects/src/notes/details.rs new file mode 100644 index 000000000..581b43251 --- /dev/null +++ b/objects/src/notes/details.rs @@ -0,0 +1,115 @@ +use miden_crypto::{ + utils::{ByteReader, ByteWriter, Deserializable, Serializable}, + Word, +}; +use vm_processor::DeserializationError; + +use super::{Digest, NoteAssets, NoteId, NoteInputs, NoteRecipient, NoteScript, Nullifier}; + +// NOTE +// ================================================================================================ + +/// A note with all the data required for it to be consumed by executing it against the transaction +/// kernel. +/// +/// Notes are created with a script, inputs, assets, and a serial number. 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 inputs allow 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, inputs, the serial number, 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)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct NoteDetails { + assets: NoteAssets, + recipient: NoteRecipient, +} + +impl NoteDetails { + // CONSTRUCTOR + // -------------------------------------------------------------------------------------------- + + /// Returns a new note created with the specified parameters. + pub fn new(assets: NoteAssets, recipient: NoteRecipient) -> Self { + Self { assets, recipient } + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the note's assets. + pub fn assets(&self) -> &NoteAssets { + &self.assets + } + + /// Returns the note's recipient. + pub fn recipient(&self) -> &NoteRecipient { + &self.recipient + } + + /// Returns the note's unique identifier. + /// + /// This value is both an unique identifier and a commitment to the note. + pub fn id(&self) -> NoteId { + NoteId::from(self) + } + + /// Returns the note's nullifier. + /// + /// This is public data, used to prevent double spend. + pub fn nullifier(&self) -> Nullifier { + Nullifier::from(self) + } + + /// Returns the note's recipient serial_num, the secret required to consume the note. + pub fn serial_num(&self) -> Word { + self.recipient.serial_num() + } + + /// Returns the note's recipient script which locks the assets of this note. + pub fn script(&self) -> &NoteScript { + self.recipient.script() + } + + /// Returns the note's recipient inputs which customizes the script's behavior. + pub fn inputs(&self) -> &NoteInputs { + self.recipient.inputs() + } + + /// Returns the note's recipient digest, which commits to its details. + /// + /// This is the public data required to create a note. + pub fn recipient_digest(&self) -> Digest { + self.recipient.digest() + } + + /// Decomposes note details into underlying assets and recipient. + pub fn into_parts(self) -> (NoteAssets, NoteRecipient) { + (self.assets, self.recipient) + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for NoteDetails { + fn write_into(&self, target: &mut W) { + let Self { assets, recipient } = self; + + assets.write_into(target); + recipient.write_into(target); + } +} + +impl Deserializable for NoteDetails { + fn read_from(source: &mut R) -> Result { + let assets = NoteAssets::read_from(source)?; + let recipient = NoteRecipient::read_from(source)?; + Ok(Self::new(assets, recipient)) + } +} diff --git a/objects/src/notes/mod.rs b/objects/src/notes/mod.rs index f23884057..d56da4c32 100644 --- a/objects/src/notes/mod.rs +++ b/objects/src/notes/mod.rs @@ -15,8 +15,8 @@ use crate::{ mod assets; pub use assets::NoteAssets; -mod note_header; -pub use note_header::NoteHeader; +mod details; +pub use details::NoteDetails; mod inputs; pub use inputs::NoteInputs; @@ -24,6 +24,9 @@ pub use inputs::NoteInputs; mod metadata; pub use metadata::NoteMetadata; +mod note_header; +pub use note_header::NoteHeader; + mod note_id; pub use note_id::NoteId; @@ -61,7 +64,7 @@ pub const NOTE_LEAF_DEPTH: u8 = NOTE_TREE_DEPTH + 1; /// /// Notes are created with a script, inputs, assets, and a serial number. 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 consumpution, i.e. the target account of a P2ID or conditions +/// 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 inputs allow for /// customization of its script. @@ -73,11 +76,9 @@ pub const NOTE_LEAF_DEPTH: u8 = NOTE_TREE_DEPTH + 1; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Note { - assets: NoteAssets, - metadata: NoteMetadata, - recipient: NoteRecipient, + header: NoteHeader, + details: NoteDetails, - id: NoteId, nullifier: Nullifier, } @@ -85,23 +86,13 @@ impl Note { // CONSTRUCTOR // -------------------------------------------------------------------------------------------- - /// Returns a new note created with the specified parameters. + /// Returns a new [Note] created with the specified parameters. pub fn new(assets: NoteAssets, metadata: NoteMetadata, recipient: NoteRecipient) -> Self { - let id = NoteId::new(recipient.digest(), assets.commitment()); - let nullifier = Nullifier::new( - recipient.script().hash(), - recipient.inputs().commitment(), - assets.commitment(), - recipient.serial_num(), - ); - - Self { - assets, - metadata, - id, - recipient, - nullifier, - } + let details = NoteDetails::new(assets, recipient); + let header = NoteHeader::new(details.id(), metadata); + let nullifier = details.nullifier(); + + Self { header, details, nullifier } } // PUBLIC ACCESSORS @@ -109,24 +100,24 @@ impl Note { /// Returns the note's assets. pub fn assets(&self) -> &NoteAssets { - &self.assets + self.details.assets() } /// Returns the note's metadata. pub fn metadata(&self) -> &NoteMetadata { - &self.metadata + self.header.metadata() } /// Returns the note's recipient. pub fn recipient(&self) -> &NoteRecipient { - &self.recipient + self.details.recipient() } /// Returns the note's unique identifier. /// /// This value is both an unique identifier and a commitment to the note. pub fn id(&self) -> NoteId { - self.id + self.header.id() } /// Returns the note's authentication hash. @@ -148,24 +139,24 @@ impl Note { /// Returns the note's recipient serial_num, the secret required to consume the note. pub fn serial_num(&self) -> Word { - self.recipient.serial_num() + self.details.serial_num() } /// Returns the note's recipient script which locks the assets of this note. pub fn script(&self) -> &NoteScript { - self.recipient.script() + self.details.script() } /// Returns the note's recipient inputs which customizes the script's behavior. pub fn inputs(&self) -> &NoteInputs { - self.recipient.inputs() + self.details.inputs() } /// Returns the note's recipient digest, which commits to its details. /// /// This is the public data required to create a note. pub fn recipient_digest(&self) -> Digest { - self.recipient.digest() + self.details.recipient_digest() } } @@ -175,27 +166,24 @@ impl Note { impl Serializable for Note { fn write_into(&self, target: &mut W) { let Self { - assets, - metadata, - recipient, + header, + details, // These attributes don't have to be serialized, they can be re-computed from the rest // of the data - id: _, nullifier: _, } = self; - assets.write_into(target); - metadata.write_into(target); - recipient.write_into(target); + header.metadata().write_into(target); + details.write_into(target); } } impl Deserializable for Note { fn read_from(source: &mut R) -> Result { - let assets = NoteAssets::read_from(source)?; let metadata = NoteMetadata::read_from(source)?; - let recipient = NoteRecipient::read_from(source)?; + let details = NoteDetails::read_from(source)?; + let (assets, recipient) = details.into_parts(); Ok(Self::new(assets, metadata, recipient)) } diff --git a/objects/src/notes/note_header.rs b/objects/src/notes/note_header.rs index 9d12e323b..6355ccf20 100644 --- a/objects/src/notes/note_header.rs +++ b/objects/src/notes/note_header.rs @@ -2,9 +2,8 @@ use alloc::vec::Vec; use super::{ ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Note, NoteId, NoteMetadata, - NoteType, Serializable, Word, + Serializable, Word, }; -use crate::NoteError; // NOTE HEADER // ================================================================================================ @@ -20,13 +19,9 @@ pub struct NoteHeader { } impl NoteHeader { - /// Returns a new [NoteHeader] object. - pub fn new(note_id: NoteId, note_metadata: NoteMetadata) -> Result { - let note_type = note_metadata.note_type(); - if note_type != NoteType::OffChain { - return Err(NoteError::InvalidNoteType(note_type)); - } - Ok(Self { note_id, note_metadata }) + /// Returns a new [NoteHeader] instantiated from the specified note ID and metadata. + pub fn new(note_id: NoteId, note_metadata: NoteMetadata) -> Self { + Self { note_id, note_metadata } } /// Returns the note's identifier. diff --git a/objects/src/notes/note_id.rs b/objects/src/notes/note_id.rs index ad1948292..4217c8852 100644 --- a/objects/src/notes/note_id.rs +++ b/objects/src/notes/note_id.rs @@ -1,7 +1,7 @@ use alloc::string::String; use core::fmt::Display; -use super::{Digest, Felt, Hasher, Note, Word}; +use super::{Digest, Felt, Hasher, NoteDetails, Word}; use crate::utils::{ serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, HexParseError, @@ -64,8 +64,8 @@ impl Display for NoteId { // CONVERSIONS INTO NOTE ID // ================================================================================================ -impl From<&Note> for NoteId { - fn from(note: &Note) -> Self { +impl From<&NoteDetails> for NoteId { + fn from(note: &NoteDetails) -> Self { Self::new(note.recipient().digest(), note.assets().commitment()) } } diff --git a/objects/src/notes/nullifier.rs b/objects/src/notes/nullifier.rs index 7d442d132..e6a130aca 100644 --- a/objects/src/notes/nullifier.rs +++ b/objects/src/notes/nullifier.rs @@ -2,8 +2,8 @@ use alloc::string::String; use core::fmt::{Debug, Display, Formatter}; use super::{ - ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, Felt, Hasher, Note, - Serializable, Word, WORD_SIZE, ZERO, + ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, Felt, Hasher, + NoteDetails, Serializable, Word, WORD_SIZE, ZERO, }; use crate::utils::{hex_to_bytes, HexParseError}; @@ -86,8 +86,8 @@ impl Debug for Nullifier { // CONVERSIONS INTO NULLIFIER // ================================================================================================ -impl From<&Note> for Nullifier { - fn from(note: &Note) -> Self { +impl From<&NoteDetails> for Nullifier { + fn from(note: &NoteDetails) -> Self { Self::new( note.script().hash(), note.inputs().commitment(), diff --git a/objects/src/notes/recipient.rs b/objects/src/notes/recipient.rs index aa03aebee..bff590b7e 100644 --- a/objects/src/notes/recipient.rs +++ b/objects/src/notes/recipient.rs @@ -13,7 +13,7 @@ use super::{ /// The recipient is not an account address, instead it is a value that describes when a note /// can be consumed. Because not all notes have predetermined consumer addresses, e.g. swap /// notes can be consumed by anyone, the recipient is defined as the code and its inputs, that -/// when sucessfully executed results in the note's consumption. +/// when successfully executed results in the note's consumption. /// /// Recipient is computed as: /// From 9a1c1d413d1862c5309fc5febe75704844b24023 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 3 May 2024 17:17:15 -0700 Subject: [PATCH 3/4] docs: update comments for note structs --- miden-lib/src/tests/test_tx.rs | 4 +- mock/src/mock/notes.rs | 6 +- objects/src/notes/details.rs | 50 +++++---------- objects/src/notes/mod.rs | 103 +++++++++++++++++++------------ objects/src/notes/note_header.rs | 20 ++---- 5 files changed, 90 insertions(+), 93 deletions(-) diff --git a/miden-lib/src/tests/test_tx.rs b/miden-lib/src/tests/test_tx.rs index 31a21129a..0385b777a 100644 --- a/miden-lib/src/tests/test_tx.rs +++ b/miden-lib/src/tests/test_tx.rs @@ -247,12 +247,12 @@ fn test_get_output_notes_hash() { end ", PUBLIC_NOTE = NoteType::Public as u8, - recipient_1 = prepare_word(&output_note_1.recipient_digest()), + recipient_1 = prepare_word(&output_note_1.recipient().digest()), tag_1 = output_note_1.metadata().tag(), asset_1 = prepare_word(&Word::from( **output_note_1.assets().iter().take(1).collect::>().first().unwrap() )), - recipient_2 = prepare_word(&output_note_2.recipient_digest()), + recipient_2 = prepare_word(&output_note_2.recipient().digest()), tag_2 = output_note_2.metadata().tag(), asset_2 = prepare_word(&Word::from( **output_note_2.assets().iter().take(1).collect::>().first().unwrap() diff --git a/mock/src/mock/notes.rs b/mock/src/mock/notes.rs index 92a7067eb..d1220acd0 100644 --- a/mock/src/mock/notes.rs +++ b/mock/src/mock/notes.rs @@ -103,10 +103,10 @@ pub fn mock_notes( end ", PUBLIC_NOTE = NoteType::Public as u8, - recipient0 = prepare_word(&created_note_1.recipient_digest()), + recipient0 = prepare_word(&created_note_1.recipient().digest()), tag0 = created_note_1.metadata().tag(), asset0 = prepare_assets(created_note_1.assets())[0], - recipient1 = prepare_word(&created_note_2.recipient_digest()), + recipient1 = prepare_word(&created_note_2.recipient().digest()), tag1 = created_note_2.metadata().tag(), asset1 = prepare_assets(created_note_2.assets())[0], ); @@ -132,7 +132,7 @@ pub fn mock_notes( end ", PUBLIC_NOTE = NoteType::Public as u8, - recipient = prepare_word(&created_note_3.recipient_digest()), + recipient = prepare_word(&created_note_3.recipient().digest()), tag = created_note_3.metadata().tag(), asset = prepare_assets(created_note_3.assets())[0], ); diff --git a/objects/src/notes/details.rs b/objects/src/notes/details.rs index 581b43251..b6cd22972 100644 --- a/objects/src/notes/details.rs +++ b/objects/src/notes/details.rs @@ -4,25 +4,14 @@ use miden_crypto::{ }; use vm_processor::DeserializationError; -use super::{Digest, NoteAssets, NoteId, NoteInputs, NoteRecipient, NoteScript, Nullifier}; +use super::{NoteAssets, NoteId, NoteInputs, NoteRecipient, NoteScript, Nullifier}; -// NOTE +// NOTE DETAILS // ================================================================================================ -/// A note with all the data required for it to be consumed by executing it against the transaction -/// kernel. +/// Details of a note consisting of assets, script, inputs, and a serial number. /// -/// Notes are created with a script, inputs, assets, and a serial number. 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 inputs allow 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, inputs, the serial number, and the kernel only -/// verifies the source account has the assets necessary for the note creation. See [NoteRecipient] -/// for more details. +/// See [super::Note] for more details. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteDetails { @@ -42,16 +31,6 @@ impl NoteDetails { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the note's assets. - pub fn assets(&self) -> &NoteAssets { - &self.assets - } - - /// Returns the note's recipient. - pub fn recipient(&self) -> &NoteRecipient { - &self.recipient - } - /// Returns the note's unique identifier. /// /// This value is both an unique identifier and a commitment to the note. @@ -59,11 +38,9 @@ impl NoteDetails { NoteId::from(self) } - /// Returns the note's nullifier. - /// - /// This is public data, used to prevent double spend. - pub fn nullifier(&self) -> Nullifier { - Nullifier::from(self) + /// Returns the note's assets. + pub fn assets(&self) -> &NoteAssets { + &self.assets } /// Returns the note's recipient serial_num, the secret required to consume the note. @@ -81,11 +58,16 @@ impl NoteDetails { self.recipient.inputs() } - /// Returns the note's recipient digest, which commits to its details. + /// Returns the note's recipient. + pub fn recipient(&self) -> &NoteRecipient { + &self.recipient + } + + /// Returns the note's nullifier. /// - /// This is the public data required to create a note. - pub fn recipient_digest(&self) -> Digest { - self.recipient.digest() + /// This is public data, used to prevent double spend. + pub fn nullifier(&self) -> Nullifier { + Nullifier::from(self) } /// Decomposes note details into underlying assets and recipient. diff --git a/objects/src/notes/mod.rs b/objects/src/notes/mod.rs index d56da4c32..dae813d23 100644 --- a/objects/src/notes/mod.rs +++ b/objects/src/notes/mod.rs @@ -62,17 +62,24 @@ pub const NOTE_LEAF_DEPTH: u8 = NOTE_TREE_DEPTH + 1; /// A note with all the data required for it to be consumed by executing it against the transaction /// kernel. /// -/// Notes are created with a script, inputs, assets, and a serial number. 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 inputs allow for -/// customization of its script. +/// Notes consist of note metadata and details. Note metadata is always public, but details may be +/// either public, encrypted, or private, depending on the note type. Note details consist of note +/// assets, script, inputs, and a serial number, the three latter grouped into a recipient object. +/// +/// Note details can be reduced to two unique identifiers: [NoteId] and [Nullifier]. The former is +/// publicly associated with a note, while the latter is known only to entities which have access +/// to full note details. +/// +/// 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 inputs allow 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, inputs, the serial number, and the kernel only -/// verifies the source account has the assets necessary for the note creation. See [NoteRecipient] -/// for more details. +/// note only with the commitment to the script, inputs, 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)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Note { @@ -98,9 +105,11 @@ impl Note { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the note's assets. - pub fn assets(&self) -> &NoteAssets { - self.details.assets() + /// Returns the note's unique identifier. + /// + /// This value is both an unique identifier and a commitment to the note. + pub fn id(&self) -> NoteId { + self.header.id() } /// Returns the note's metadata. @@ -108,16 +117,36 @@ impl Note { self.header.metadata() } + /// 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 inputs which customizes the script's behavior. + pub fn inputs(&self) -> &NoteInputs { + self.details.inputs() + } + /// Returns the note's recipient. pub fn recipient(&self) -> &NoteRecipient { self.details.recipient() } - /// Returns the note's unique identifier. + /// Returns the note's nullifier. /// - /// This value is both an unique identifier and a commitment to the note. - pub fn id(&self) -> NoteId { - self.header.id() + /// This is public data, used to prevent double spend. + pub fn nullifier(&self) -> Nullifier { + self.nullifier } /// Returns the note's authentication hash. @@ -129,34 +158,32 @@ impl Note { pub fn authentication_hash(&self) -> Digest { Hasher::merge(&[self.id().inner(), Word::from(self.metadata()).into()]) } +} - /// Returns the note's nullifier. - /// - /// This is public data, used to prevent double spend. - pub fn nullifier(&self) -> Nullifier { - self.nullifier - } +// CONVERSIONS FROM NOTE +// ================================================================================================ - /// Returns the note's recipient serial_num, the secret required to consume the note. - pub fn serial_num(&self) -> Word { - self.details.serial_num() +impl From<&Note> for NoteHeader { + fn from(note: &Note) -> Self { + note.header } +} - /// Returns the note's recipient script which locks the assets of this note. - pub fn script(&self) -> &NoteScript { - self.details.script() +impl From for NoteHeader { + fn from(note: Note) -> Self { + note.header } +} - /// Returns the note's recipient inputs which customizes the script's behavior. - pub fn inputs(&self) -> &NoteInputs { - self.details.inputs() +impl From<&Note> for NoteDetails { + fn from(note: &Note) -> Self { + note.details.clone() } +} - /// Returns the note's recipient digest, which commits to its details. - /// - /// This is the public data required to create a note. - pub fn recipient_digest(&self) -> Digest { - self.details.recipient_digest() +impl From for NoteDetails { + fn from(note: Note) -> Self { + note.details } } @@ -169,11 +196,11 @@ impl Serializable for Note { header, details, - // These attributes don't have to be serialized, they can be re-computed from the rest - // of the data + // nullifier is not serialized as it can be computed from the rest of the data nullifier: _, } = self; + // only metadata is serialized as note ID can be computed from note details header.metadata().write_into(target); details.write_into(target); } diff --git a/objects/src/notes/note_header.rs b/objects/src/notes/note_header.rs index 6355ccf20..a11b2405b 100644 --- a/objects/src/notes/note_header.rs +++ b/objects/src/notes/note_header.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use super::{ - ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Note, NoteId, NoteMetadata, + ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, NoteId, NoteMetadata, Serializable, Word, }; @@ -37,6 +37,9 @@ impl NoteHeader { } } +// CONVERSIONS FROM NOTE HEADER +// ================================================================================================ + impl From for [Felt; 8] { fn from(note_header: NoteHeader) -> Self { (¬e_header).into() @@ -86,21 +89,6 @@ impl From<&NoteHeader> for [u8; 64] { } } -impl From for NoteHeader { - fn from(note: Note) -> Self { - (¬e).into() - } -} - -impl From<&Note> for NoteHeader { - fn from(note: &Note) -> Self { - Self { - note_id: note.id(), - note_metadata: *note.metadata(), - } - } -} - // SERIALIZATION // ================================================================================================ From 5b1b57c51f42ed0b645c2fc76ba84c52392f21d9 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sun, 5 May 2024 22:36:44 -0700 Subject: [PATCH 4/4] chore: update changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb546cda..838cd3c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,11 @@ ## 0.3.0 (TBD) +* Introduce the `miden-bench-tx` crate used for transactions benchmarking (#577). * [BREAKING] Removed the transaction script root output from the transaction kernel (#608). * [BREAKING] Refactored account update details, moved `Block` to `miden-objects` (#618, #621). -* [BREAKING] Changed type of `version` and `timestamp` fields to `u32`, moved `version` to the beginning of block header - (#639). -* Introduce the `miden-bench-tx` crate used for transactions benchmarking (#577). +* [BREAKING] Changed type of `version` and `timestamp` fields to `u32`, moved `version` to the beginning of block header (#639). +* [BREAKING] Renamed `NoteEnvelope` into `NoteHeader` and introduced `NoteDetails` (#664). ## 0.2.3 (2024-04-26) - `miden-tx` crate only