diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ab16692d..d404f5a5f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## v0.16.0 (TBD) +### Changes + +- [BREAKING] Replaced `P2idNote::create` factory with a `bon`-based typestate builder. Construct via `P2idNote::builder().sender(..).target(..)..build()?` and convert with `Note::from(_)`. Added `.asset()`, `.attachment()`, `.generate_serial_number()` builder methods and a `SCRIPT_ROOT` static accessor ([#2283](https://github.com/0xMiden/protocol/issues/2283)). + ## v0.15.0 (2026-05-22) ### Features diff --git a/crates/miden-standards/src/note/mod.rs b/crates/miden-standards/src/note/mod.rs index 6335fcb407..7b79361a18 100644 --- a/crates/miden-standards/src/note/mod.rs +++ b/crates/miden-standards/src/note/mod.rs @@ -16,6 +16,7 @@ mod mint; pub use mint::{MintNote, MintNoteStorage}; mod p2id; +pub use p2id::SCRIPT_ROOT as P2ID_SCRIPT_ROOT; pub use p2id::{P2idNote, P2idNoteStorage}; mod p2ide; diff --git a/crates/miden-standards/src/note/p2id.rs b/crates/miden-standards/src/note/p2id.rs index 41f06c3e33..ce67cdc554 100644 --- a/crates/miden-standards/src/note/p2id.rs +++ b/crates/miden-standards/src/note/p2id.rs @@ -8,6 +8,7 @@ use miden_protocol::errors::NoteError; use miden_protocol::note::{ Note, NoteAssets, + NoteAttachment, NoteAttachments, NoteRecipient, NoteScript, @@ -21,6 +22,7 @@ use miden_protocol::utils::sync::LazyLock; use miden_protocol::{Felt, Word}; use crate::StandardsLib; + // NOTE SCRIPT // ================================================================================================ @@ -35,12 +37,27 @@ static P2ID_SCRIPT: LazyLock = LazyLock::new(|| { .expect("Standards library contains P2ID note script procedure") }); +/// The root hash of the P2ID note script. +pub static SCRIPT_ROOT: LazyLock = LazyLock::new(|| P2ID_SCRIPT.root()); + // P2ID NOTE // ================================================================================================ -/// TODO: add docs -pub struct P2idNote; +/// A Pay-to-ID note: transfers `assets` from `sender` to `target`. Only `target` can consume +/// the note and claim its assets. +/// +/// Construct via [`Self::builder`]. Convert into a protocol [`Note`] via `Note::from(_)`. +#[derive(Debug, Clone)] +pub struct P2idNote { + sender: AccountId, + storage: P2idNoteStorage, + serial_number: Word, + note_type: NoteType, + assets: NoteAssets, + attachments: NoteAttachments, +} +#[bon::bon] impl P2idNote { // CONSTANTS // -------------------------------------------------------------------------------------------- @@ -48,52 +65,159 @@ impl P2idNote { /// Expected number of storage items of the P2ID note. pub const NUM_STORAGE_ITEMS: usize = P2idNoteStorage::NUM_ITEMS; + // BUILDER + // -------------------------------------------------------------------------------------------- + + /// Builds a new [`P2idNote`]. + /// + /// Use `.asset()` and `.attachment()` to add items individually, `.target()` to set the + /// recipient, and `.generate_serial_number()` to draw the serial from an RNG. + /// + /// # Errors + /// + /// Returns an error if `assets` or `attachments` exceed their protocol limits (see + /// [`NoteAssets::new`] and [`NoteAttachments::new`]). + #[builder] + pub fn new( + #[builder(field)] assets: Vec, + #[builder(field)] attachments: Vec, + sender: AccountId, + #[builder(name = target, with = |target: AccountId| P2idNoteStorage::new(target))] + storage: P2idNoteStorage, + serial_number: Word, + #[builder(default = NoteType::Private)] note_type: NoteType, + ) -> Result { + let assets = NoteAssets::new(assets)?; + let attachments = NoteAttachments::new(attachments)?; + + Ok(Self { + sender, + storage, + serial_number, + note_type, + assets, + attachments, + }) + } + // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the script of the P2ID (Pay-to-ID) note. + /// Returns the compiled P2ID note script. pub fn script() -> NoteScript { P2ID_SCRIPT.clone() } - /// Returns the P2ID (Pay-to-ID) note script root. + /// Returns the P2ID note script root. pub fn script_root() -> NoteScriptRoot { P2ID_SCRIPT.root() } - // BUILDERS - // -------------------------------------------------------------------------------------------- + /// Returns the account ID of the note sender. + pub fn sender(&self) -> AccountId { + self.sender + } - /// Generates a P2ID note - Pay-to-ID note. - /// - /// This script enables the transfer of assets from the `sender` account to the `target` account - /// by specifying the target's account ID. - /// - /// The passed-in `rng` is used to generate a serial number for the note. The returned note's - /// tag is set to the target's account ID. - /// - /// # Errors - /// Returns an error if deserialization or compilation of the `P2ID` script fails. - pub fn create( - sender: AccountId, - target: AccountId, - assets: Vec, - note_type: NoteType, - attachments: NoteAttachments, - rng: &mut R, - ) -> Result { - let serial_num = rng.draw_word(); - let recipient = P2idNoteStorage::new(target).into_recipient(serial_num); + /// Returns the P2ID note's storage. + pub fn storage(&self) -> &P2idNoteStorage { + &self.storage + } - let tag = NoteTag::with_account_target(target); + /// Returns the target account ID. + pub fn target(&self) -> AccountId { + self.storage.target() + } - let metadata = PartialNoteMetadata::new(sender, note_type).with_tag(tag); - let vault = NoteAssets::new(assets)?; + /// Returns the serial number of this note. + pub fn serial_number(&self) -> Word { + self.serial_number + } + + /// Returns the note type (public or private). + pub fn note_type(&self) -> NoteType { + self.note_type + } - Ok(Note::with_attachments(vault, metadata, recipient, attachments)) + /// Returns the assets carried by this note. + pub fn assets(&self) -> &NoteAssets { + &self.assets + } + + /// Returns the attachments carried by this note. + pub fn attachments(&self) -> &NoteAttachments { + &self.attachments } } +// BUILDER EXTENSIONS +// ================================================================================================ + +impl P2idNoteBuilder { + /// Appends an asset to the note's assets. + pub fn asset(mut self, asset: impl Into) -> Self { + self.assets.push(asset.into()); + self + } + + /// Appends an attachment to the note's attachments. + pub fn attachment(mut self, attachment: impl Into) -> Self { + self.attachments.push(attachment.into()); + self + } +} + +impl P2idNoteBuilder +where + S::SerialNumber: p2id_note_builder::IsUnset, +{ + /// Draws a serial number from `rng` and sets it on the builder. + pub fn generate_serial_number( + self, + rng: &mut impl FeltRng, + ) -> P2idNoteBuilder> { + self.serial_number(rng.draw_word()) + } +} + +// CONVERSIONS +// ================================================================================================ + +/// Converts a [`P2idNote`] into the protocol [`Note`]. +impl From for Note { + fn from(note: P2idNote) -> Self { + let recipient = note.storage.into_recipient(note.serial_number); + let tag = NoteTag::with_account_target(note.storage.target()); + let metadata = PartialNoteMetadata::new(note.sender, note.note_type).with_tag(tag); + Note::with_attachments(note.assets, metadata, recipient, note.attachments) + } +} + +/// Parses a protocol [`Note`] back into a [`P2idNote`]. Fails if the note's script root is not +/// the P2ID script root. +impl TryFrom<&Note> for P2idNote { + type Error = NoteError; + + fn try_from(note: &Note) -> Result { + if note.recipient().script().root() != P2idNote::script_root() { + return Err(NoteError::other("note script root does not match P2ID script root")); + } + + let storage = P2idNoteStorage::try_from(note.recipient().storage().items())?; + + Ok(Self { + sender: note.metadata().sender(), + storage, + serial_number: note.recipient().serial_num(), + note_type: note.metadata().note_type(), + assets: note.assets().clone(), + attachments: note.attachments().clone(), + }) + } +} + +// P2ID NOTE STORAGE +// ================================================================================================ + /// Canonical storage representation for a P2ID note. /// /// Contains the identifier of the target account that is authorized @@ -164,10 +288,15 @@ impl TryFrom<&[Felt]> for P2idNoteStorage { mod tests { use miden_protocol::Felt; use miden_protocol::account::{AccountId, AccountIdVersion, AccountType}; + use miden_protocol::asset::FungibleAsset; + use miden_protocol::crypto::rand::RandomCoin; use miden_protocol::errors::NoteError; use super::*; + // STORAGE TESTS + // -------------------------------------------------------------------------------------------- + #[test] fn try_from_valid_storage_succeeds() { let target = AccountId::dummy([1u8; 15], AccountIdVersion::Version1, AccountType::Private); @@ -205,4 +334,118 @@ mod tests { assert!(matches!(err, NoteError::Other { source: Some(_), .. })); } + + // BUILDER TESTS + // -------------------------------------------------------------------------------------------- + + fn dummy_sender() -> AccountId { + AccountId::dummy([1u8; 15], AccountIdVersion::Version1, AccountType::Private) + } + + fn dummy_target() -> AccountId { + AccountId::dummy([2u8; 15], AccountIdVersion::Version1, AccountType::Private) + } + + fn dummy_faucet_a() -> AccountId { + AccountId::dummy([3u8; 15], AccountIdVersion::Version1, AccountType::Public) + } + + fn dummy_faucet_b() -> AccountId { + AccountId::dummy([4u8; 15], AccountIdVersion::Version1, AccountType::Public) + } + + /// Minimal builder — only required fields, defaults for the rest. + #[test] + fn builder_minimal() { + let note = P2idNote::builder() + .sender(dummy_sender()) + .target(dummy_target()) + .serial_number(Word::default()) + .build() + .unwrap(); + + assert_eq!(note.sender(), dummy_sender()); + assert_eq!(note.target(), dummy_target()); + assert_eq!(note.note_type(), NoteType::Private); // default + assert_eq!(note.assets().num_assets(), 0); + assert_eq!(note.attachments().num_attachments(), 0); + } + + /// `.asset()` accumulates across multiple calls (one entry per distinct faucet). + #[test] + fn builder_asset_accumulates() { + let a = FungibleAsset::new(dummy_faucet_a(), 100).unwrap(); + let b = FungibleAsset::new(dummy_faucet_b(), 200).unwrap(); + + let note = P2idNote::builder() + .sender(dummy_sender()) + .target(dummy_target()) + .serial_number(Word::default()) + .asset(a) + .asset(b) + .build() + .unwrap(); + + assert_eq!(note.assets().num_assets(), 2); + } + + /// Two assets from the same faucet are rejected by `NoteAssets::new`. + #[test] + fn builder_rejects_duplicate_faucet() { + let a = FungibleAsset::new(dummy_faucet_a(), 100).unwrap(); + let b = FungibleAsset::new(dummy_faucet_a(), 200).unwrap(); + + let err = P2idNote::builder() + .sender(dummy_sender()) + .target(dummy_target()) + .serial_number(Word::default()) + .asset(a) + .asset(b) + .build() + .expect_err("duplicate faucet should be rejected"); + + assert!(matches!(err, NoteError::DuplicateFungibleAsset(_))); + } + + /// `.generate_serial_number(rng)` draws from RNG and sets the field. + #[test] + fn builder_generate_serial_number() { + let mut rng = RandomCoin::new(Word::default()); + let note = P2idNote::builder() + .sender(dummy_sender()) + .target(dummy_target()) + .generate_serial_number(&mut rng) + .build() + .unwrap(); + + assert_ne!(note.serial_number(), Word::default()); + } + + /// `SCRIPT_ROOT` static must yield the same value as `script_root()`. + #[test] + fn script_root_static_matches_accessor() { + assert_eq!(*SCRIPT_ROOT, P2idNote::script_root()); + } + + /// `From for Note` is infallible; round-trips through `TryFrom<&Note>`. + #[test] + fn from_p2id_roundtrips_via_try_from() { + let original = P2idNote::builder() + .sender(dummy_sender()) + .target(dummy_target()) + .serial_number(Word::default()) + .asset(FungibleAsset::new(dummy_faucet_a(), 42).unwrap()) + .note_type(NoteType::Public) + .build() + .unwrap(); + + let note: Note = original.clone().into(); + let parsed = P2idNote::try_from(¬e).expect("roundtrip should succeed"); + + assert_eq!(parsed.sender(), original.sender()); + assert_eq!(parsed.target(), original.target()); + assert_eq!(parsed.serial_number(), original.serial_number()); + assert_eq!(parsed.note_type(), original.note_type()); + assert_eq!(parsed.assets().num_assets(), original.assets().num_assets()); + } } diff --git a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs index 4cdb62da64..8b3d38de2a 100644 --- a/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs +++ b/crates/miden-testing/src/kernel_tests/block/proposed_block_errors.rs @@ -9,7 +9,7 @@ use miden_protocol::asset::FungibleAsset; use miden_protocol::block::{BlockInputs, BlockNumber, ProposedBlock}; use miden_protocol::crypto::merkle::SparseMerklePath; use miden_protocol::errors::ProposedBlockError; -use miden_protocol::note::{NoteAttachments, NoteInclusionProof, NoteType}; +use miden_protocol::note::{Note, NoteInclusionProof, NoteType}; use miden_standards::note::P2idNote; use miden_tx::LocalTransactionProver; @@ -351,14 +351,13 @@ async fn proposed_block_fails_on_invalid_proof_or_missing_note_inclusion_referen let mut builder = MockChain::builder(); let account0 = builder.add_existing_mock_account(Auth::IncrNonce)?; let account1 = builder.add_existing_mock_account(Auth::IncrNonce)?; - let p2id_note = P2idNote::create( - account0.id(), - account1.id(), - vec![], - NoteType::Private, - NoteAttachments::default(), - builder.rng_mut(), - )?; + let p2id_note: Note = P2idNote::builder() + .sender(account0.id()) + .target(account1.id()) + .note_type(NoteType::Private) + .generate_serial_number(builder.rng_mut()) + .build()? + .into(); let spawn_note = builder.add_spawn_note([&p2id_note])?; let mut chain = builder.build()?; diff --git a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs index 865d0a244c..9456b31724 100644 --- a/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs +++ b/crates/miden-testing/src/kernel_tests/block/proven_block_success.rs @@ -8,7 +8,7 @@ use miden_protocol::batch::BatchNoteTree; use miden_protocol::block::account_tree::AccountTree; use miden_protocol::block::{BlockInputs, BlockNoteIndex, BlockNoteTree, ProposedBlock}; use miden_protocol::crypto::merkle::smt::Smt; -use miden_protocol::note::{NoteAttachments, NoteType}; +use miden_protocol::note::{Note, NoteType}; use miden_protocol::transaction::InputNoteCommitment; use miden_standards::note::P2idNote; @@ -32,38 +32,38 @@ async fn proven_block_success() -> anyhow::Result<()> { let account2 = builder.add_existing_mock_account_with_assets(Auth::IncrNonce, [asset])?; let account3 = builder.add_existing_mock_account_with_assets(Auth::IncrNonce, [asset])?; - let output_note0 = P2idNote::create( - account0.id(), - account0.id(), - vec![asset], - NoteType::Private, - NoteAttachments::default(), - builder.rng_mut(), - )?; - let output_note1 = P2idNote::create( - account1.id(), - account1.id(), - vec![asset], - NoteType::Private, - NoteAttachments::default(), - builder.rng_mut(), - )?; - let output_note2 = P2idNote::create( - account2.id(), - account2.id(), - vec![asset], - NoteType::Private, - NoteAttachments::default(), - builder.rng_mut(), - )?; - let output_note3 = P2idNote::create( - account3.id(), - account3.id(), - vec![asset], - NoteType::Private, - NoteAttachments::default(), - builder.rng_mut(), - )?; + let output_note0: Note = P2idNote::builder() + .sender(account0.id()) + .target(account0.id()) + .note_type(NoteType::Private) + .asset(asset) + .generate_serial_number(builder.rng_mut()) + .build()? + .into(); + let output_note1: Note = P2idNote::builder() + .sender(account1.id()) + .target(account1.id()) + .note_type(NoteType::Private) + .asset(asset) + .generate_serial_number(builder.rng_mut()) + .build()? + .into(); + let output_note2: Note = P2idNote::builder() + .sender(account2.id()) + .target(account2.id()) + .note_type(NoteType::Private) + .asset(asset) + .generate_serial_number(builder.rng_mut()) + .build()? + .into(); + let output_note3: Note = P2idNote::builder() + .sender(account3.id()) + .target(account3.id()) + .note_type(NoteType::Private) + .asset(asset) + .generate_serial_number(builder.rng_mut()) + .build()? + .into(); let input_note0 = builder.add_spawn_note([&output_note0])?; let input_note1 = builder.add_spawn_note([&output_note1])?; diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs index ba09751323..5b99769523 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account_interface.rs @@ -45,14 +45,14 @@ use crate::{Auth, MockChain, TransactionContextBuilder, TxContextInput}; #[tokio::test] async fn check_note_consumability_standard_notes_success() -> anyhow::Result<()> { - let p2id_note = P2idNote::create( - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap(), - vec![FungibleAsset::mock(10)], - NoteType::Public, - Default::default(), - &mut RandomCoin::new(Word::from([2u32; 4])), - )?; + let p2id_note: Note = P2idNote::builder() + .sender(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap()) + .note_type(NoteType::Public) + .asset(FungibleAsset::mock(10)) + .generate_serial_number(&mut RandomCoin::new(Word::from([2u32; 4]))) + .build()? + .into(); let p2ide_note = P2ideNote::create( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap(), diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index d2997342aa..3adad3b598 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -886,23 +886,24 @@ async fn test_get_asset_info() -> anyhow::Result<()> { let mock_chain = builder.build()?; - let output_note_0 = P2idNote::create( - account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?, - vec![fungible_asset_0], - NoteType::Public, - NoteAttachments::default(), - &mut RandomCoin::new(Word::from([1, 2, 3, 4u32])), - )?; + let output_note_0: Note = P2idNote::builder() + .sender(account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?) + .note_type(NoteType::Public) + .asset(fungible_asset_0) + .generate_serial_number(&mut RandomCoin::new(Word::from([1, 2, 3, 4u32]))) + .build()? + .into(); - let output_note_1 = P2idNote::create( - account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?, - vec![fungible_asset_0, fungible_asset_1], - NoteType::Public, - NoteAttachments::default(), - &mut RandomCoin::new(Word::from([4, 3, 2, 1u32])), - )?; + let output_note_1: Note = P2idNote::builder() + .sender(account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?) + .note_type(NoteType::Public) + .asset(fungible_asset_0) + .asset(fungible_asset_1) + .generate_serial_number(&mut RandomCoin::new(Word::from([4, 3, 2, 1u32]))) + .build()? + .into(); let tx_script_src = &format!( r#" @@ -1016,14 +1017,14 @@ async fn test_get_recipient_and_metadata() -> anyhow::Result<()> { let mock_chain = builder.build()?; - let output_note = P2idNote::create( - account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?, - vec![FungibleAsset::mock(5)], - NoteType::Public, - NoteAttachments::default(), - &mut RandomCoin::new(Word::from([1, 2, 3, 4u32])), - )?; + let output_note: Note = P2idNote::builder() + .sender(account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?) + .note_type(NoteType::Public) + .asset(FungibleAsset::mock(5)) + .generate_serial_number(&mut RandomCoin::new(Word::from([1, 2, 3, 4u32]))) + .build()? + .into(); let tx_script_src = &format!( r#" diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index c82587ced1..1eb9fc62e7 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -470,14 +470,13 @@ async fn user_code_can_abort_transaction_with_summary() -> anyhow::Result<()> { // Consume and create a note so the input and outputs notes commitment is not the empty word. let mut rng = RandomCoin::new(Word::empty()); - let output_note = P2idNote::create( - account.id(), - account.id(), - vec![], - NoteType::Private, - NoteAttachments::default(), - &mut rng, - )?; + let output_note: Note = P2idNote::builder() + .sender(account.id()) + .target(account.id()) + .note_type(NoteType::Private) + .generate_serial_number(&mut rng) + .build()? + .into(); let input_note = create_spawn_note(vec![&output_note])?; let mut builder = MockChain::builder(); @@ -515,14 +514,13 @@ async fn tx_summary_commitment_is_signed_by_falcon_auth() -> anyhow::Result<()> auth_scheme: AuthScheme::Falcon512Poseidon2, })?; let mut rng = RandomCoin::new(Word::empty()); - let p2id_note = P2idNote::create( - account.id(), - account.id(), - vec![], - NoteType::Private, - NoteAttachments::default(), - &mut rng, - )?; + let p2id_note: Note = P2idNote::builder() + .sender(account.id()) + .target(account.id()) + .note_type(NoteType::Private) + .generate_serial_number(&mut rng) + .build()? + .into(); let spawn_note = builder.add_spawn_note([&p2id_note])?; let chain = builder.build()?; @@ -577,14 +575,13 @@ async fn tx_summary_commitment_is_signed_by_ecdsa_auth() -> anyhow::Result<()> { let account = builder .add_existing_mock_account(Auth::BasicAuth { auth_scheme: AuthScheme::EcdsaK256Keccak })?; let mut rng = RandomCoin::new(Word::empty()); - let p2id_note = P2idNote::create( - account.id(), - account.id(), - vec![], - NoteType::Private, - NoteAttachments::default(), - &mut rng, - )?; + let p2id_note: Note = P2idNote::builder() + .sender(account.id()) + .target(account.id()) + .note_type(NoteType::Private) + .generate_serial_number(&mut rng) + .build()? + .into(); let spawn_note = builder.add_spawn_note([&p2id_note])?; let chain = builder.build()?; diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 4f5aa42276..396284d87f 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -685,17 +685,18 @@ impl MockChainBuilder { &mut self, sender_account_id: AccountId, target_account_id: AccountId, - asset: &[Asset], + assets: &[Asset], note_type: NoteType, ) -> Result { - let note = P2idNote::create( - sender_account_id, - target_account_id, - asset.to_vec(), - note_type, - NoteAttachments::default(), - &mut self.rng, - )?; + let mut builder = P2idNote::builder() + .sender(sender_account_id) + .target(target_account_id) + .note_type(note_type) + .generate_serial_number(&mut self.rng); + for asset in assets { + builder = builder.asset(*asset); + } + let note: Note = builder.build()?.into(); self.add_output_note(RawOutputNote::Full(note.clone())); Ok(note) diff --git a/crates/miden-testing/tests/auth/hybrid_multisig.rs b/crates/miden-testing/tests/auth/hybrid_multisig.rs index 222cf79dbb..63d25fa283 100644 --- a/crates/miden-testing/tests/auth/hybrid_multisig.rs +++ b/crates/miden-testing/tests/auth/hybrid_multisig.rs @@ -3,7 +3,7 @@ use miden_processor::crypto::random::RandomCoin; use miden_protocol::account::auth::{AuthScheme, AuthSecretKey, PublicKey}; use miden_protocol::account::{Account, AccountBuilder, AccountProcedureRoot, AccountType}; use miden_protocol::asset::FungibleAsset; -use miden_protocol::note::NoteType; +use miden_protocol::note::{Note, NoteType}; use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; use miden_protocol::transaction::RawOutputNote; use miden_protocol::vm::AdviceMap; @@ -487,14 +487,14 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { } // Create a new output note for the second transaction with new signers - let output_note_new = P2idNote::create( - updated_multisig_account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap(), - vec![output_note_asset], - NoteType::Public, - Default::default(), - &mut RandomCoin::new(Word::empty()), - )?; + let output_note_new: Note = P2idNote::builder() + .sender(updated_multisig_account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap()) + .note_type(NoteType::Public) + .asset(output_note_asset) + .generate_serial_number(&mut RandomCoin::new(Word::empty())) + .build()? + .into(); // Create a new spawn note for the second transaction let input_note_new = create_spawn_note([&output_note_new])?; diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index 629bd032dc..e722d9a469 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -9,7 +9,7 @@ use miden_protocol::account::{ AccountType, }; use miden_protocol::asset::{AssetCallbackFlag, AssetVaultKey, FungibleAsset}; -use miden_protocol::note::NoteType; +use miden_protocol::note::{Note, NoteType}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, @@ -613,14 +613,14 @@ async fn test_multisig_update_signers(#[case] auth_scheme: AuthScheme) -> anyhow } // Create a new output note for the second transaction with new signers - let output_note_new = P2idNote::create( - updated_multisig_account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap(), - vec![output_note_asset], - NoteType::Public, - Default::default(), - &mut RandomCoin::new(Word::empty()), - )?; + let output_note_new: Note = P2idNote::builder() + .sender(updated_multisig_account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap()) + .note_type(NoteType::Public) + .asset(output_note_asset) + .generate_serial_number(&mut RandomCoin::new(Word::empty())) + .build()? + .into(); // Create a new spawn note for the second transaction let input_note_new = create_spawn_note([&output_note_new])?; @@ -1159,14 +1159,14 @@ async fn test_multisig_proc_threshold_overrides( // Create output note to send 5 units from the account let asset = FungibleAsset::mock(5); - let output_note = P2idNote::create( - multisig_account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap(), - vec![asset], - NoteType::Public, - Default::default(), - &mut RandomCoin::new(Word::from([Felt::new_unchecked(42); 4])), - )?; + let output_note: Note = P2idNote::builder() + .sender(multisig_account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap()) + .note_type(NoteType::Public) + .asset(asset) + .generate_serial_number(&mut RandomCoin::new(Word::from([Felt::new_unchecked(42); 4]))) + .build()? + .into(); let multisig_account_interface = AccountInterface::from_account(&multisig_account); let send_note_transaction_script = multisig_account_interface.build_send_notes_script(&[output_note.clone().into()], None)?; diff --git a/crates/miden-testing/tests/auth/multisig_smart.rs b/crates/miden-testing/tests/auth/multisig_smart.rs index dbafddee0d..c60bf3fe3f 100644 --- a/crates/miden-testing/tests/auth/multisig_smart.rs +++ b/crates/miden-testing/tests/auth/multisig_smart.rs @@ -2,7 +2,7 @@ use miden_processor::advice::AdviceInputs; use miden_protocol::account::auth::{AuthScheme, PublicKey}; use miden_protocol::account::{Account, AccountBuilder, AccountId, AccountType}; use miden_protocol::asset::FungibleAsset; -use miden_protocol::note::NoteType; +use miden_protocol::note::{Note, NoteType}; use miden_protocol::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET; use miden_protocol::transaction::TransactionScript; use miden_protocol::vm::AdviceMap; @@ -241,14 +241,14 @@ async fn test_multisig_smart_enforces_note_restrictions_on_tx_with_output_notes( )], )?; - let output_note = P2idNote::create( - multisig_account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap(), - vec![FungibleAsset::mock(5)], - NoteType::Public, - Default::default(), - &mut RandomCoin::new(Word::from([Felt::new_unchecked(7); 4])), - )?; + let output_note: Note = P2idNote::builder() + .sender(multisig_account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE.try_into().unwrap()) + .note_type(NoteType::Public) + .asset(FungibleAsset::mock(5)) + .generate_serial_number(&mut RandomCoin::new(Word::from([Felt::new_unchecked(7); 4]))) + .build()? + .into(); let send_note_script = AccountInterface::from_account(&multisig_account) .build_send_notes_script(&[output_note.clone().into()], None)?; diff --git a/crates/miden-testing/tests/scripts/p2id.rs b/crates/miden-testing/tests/scripts/p2id.rs index 8c4b376101..f781f180d5 100644 --- a/crates/miden-testing/tests/scripts/p2id.rs +++ b/crates/miden-testing/tests/scripts/p2id.rs @@ -2,7 +2,7 @@ use miden_protocol::account::Account; use miden_protocol::account::auth::AuthScheme; use miden_protocol::asset::{Asset, AssetVault, FungibleAsset}; use miden_protocol::crypto::rand::RandomCoin; -use miden_protocol::note::{NoteAttachments, NoteTag, NoteType}; +use miden_protocol::note::{Note, NoteTag, NoteType}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, @@ -222,23 +222,23 @@ async fn test_create_consume_multiple_notes() -> anyhow::Result<()> { let asset_1 = FungibleAsset::mock(10); let asset_2 = FungibleAsset::mock(5); - let output_note_1 = P2idNote::create( - account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into()?, - vec![asset_1], - NoteType::Public, - NoteAttachments::default(), - &mut RandomCoin::new(Word::from([1, 2, 3, 4u32])), - )?; - - let output_note_2 = P2idNote::create( - account.id(), - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?, - vec![asset_2], - NoteType::Public, - NoteAttachments::default(), - &mut RandomCoin::new(Word::from([4, 3, 2, 1u32])), - )?; + let output_note_1: Note = P2idNote::builder() + .sender(account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2.try_into()?) + .note_type(NoteType::Public) + .asset(asset_1) + .generate_serial_number(&mut RandomCoin::new(Word::from([1, 2, 3, 4u32]))) + .build()? + .into(); + + let output_note_2: Note = P2idNote::builder() + .sender(account.id()) + .target(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into()?) + .note_type(NoteType::Public) + .asset(asset_2) + .generate_serial_number(&mut RandomCoin::new(Word::from([4, 3, 2, 1u32]))) + .build()? + .into(); let tx_script_src = &format!( " @@ -366,14 +366,14 @@ async fn test_p2id_new_constructor() -> anyhow::Result<()> { let tx_script = CodeBuilder::default().compile_tx_script(&tx_script_src)?; // Build expected output note - let expected_output_note = P2idNote::create( - sender_account.id(), - target_account.id(), - vec![FungibleAsset::mock(50)], - NoteType::Public, - NoteAttachments::default(), - &mut RandomCoin::new(serial_num), - )?; + let expected_output_note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(target_account.id()) + .note_type(NoteType::Public) + .asset(FungibleAsset::mock(50)) + .generate_serial_number(&mut RandomCoin::new(serial_num)) + .build()? + .into(); let tx_context = mock_chain .build_tx_context(sender_account.id(), &[], &[])? diff --git a/crates/miden-testing/tests/scripts/send_note.rs b/crates/miden-testing/tests/scripts/send_note.rs index e1565146ea..b771b4da61 100644 --- a/crates/miden-testing/tests/scripts/send_note.rs +++ b/crates/miden-testing/tests/scripts/send_note.rs @@ -84,14 +84,17 @@ async fn test_send_note_script_basic_wallet() -> anyhow::Result<()> { "test should use max num of attachments" ); - let p2id_note = P2idNote::create( - sender_basic_wallet_account.id(), - sender_basic_wallet_account.id(), - vec![sent_asset0, sent_asset2], - NoteType::Public, - attachments, - &mut rng, - )?; + let mut builder = P2idNote::builder() + .sender(sender_basic_wallet_account.id()) + .target(sender_basic_wallet_account.id()) + .note_type(NoteType::Public) + .asset(sent_asset0) + .asset(sent_asset2) + .generate_serial_number(&mut rng); + for attachment in attachments.iter().cloned() { + builder = builder.attachment(attachment); + } + let p2id_note: Note = builder.build()?.into(); let partial_note = PartialNote::from(p2id_note.clone()); let expiration_delta = 10u16;