diff --git a/CHANGELOG.md b/CHANGELOG.md index d278cc076..102cdc5ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,11 @@ ### Features * [BREAKING] Added `AccountRecord` with information about the account's status (#600). +* [BREAKING] Added `TransactionRequestBuilder` for building `TransactionRequest` (#605). * Added caching for foreign account code (#597). * Added support for unauthenticated notes consumption in the CLI (#609). ### Fixes - * Added Sync Loop to Integration Tests for Small Speedup (#590). ### Changes diff --git a/bin/miden-cli/src/commands/new_transactions.rs b/bin/miden-cli/src/commands/new_transactions.rs index 3a123e7c4..d9ee12de8 100644 --- a/bin/miden-cli/src/commands/new_transactions.rs +++ b/bin/miden-cli/src/commands/new_transactions.rs @@ -7,7 +7,8 @@ use miden_client::{ crypto::{Digest, FeltRng}, notes::{build_swap_tag, get_input_note_with_id_prefix, NoteType as MidenNoteType}, transactions::{ - PaymentTransactionData, SwapTransactionData, TransactionRequest, TransactionResult, + PaymentTransactionData, SwapTransactionData, TransactionRequest, TransactionRequestBuilder, + TransactionResult, }, Client, }; @@ -68,13 +69,14 @@ impl MintCmd { let target_account_id = parse_account_id(&client, self.target_account_id.as_str()).await?; - let transaction_request = TransactionRequest::mint_fungible_asset( + let transaction_request = TransactionRequestBuilder::mint_fungible_asset( fungible_asset, target_account_id, (&self.note_type).into(), client.rng(), ) - .map_err(|err| err.to_string())?; + .map_err(|err| err.to_string())? + .build(); execute_transaction( &mut client, @@ -138,13 +140,14 @@ impl SendCmd { target_account_id, ); - let transaction_request = TransactionRequest::pay_to_id( + let transaction_request = TransactionRequestBuilder::pay_to_id( payment_transaction, self.recall_height, (&self.note_type).into(), client.rng(), ) - .map_err(|err| err.to_string())?; + .map_err(|err| err.to_string())? + .build(); execute_transaction( &mut client, @@ -205,12 +208,13 @@ impl SwapCmd { requested_fungible_asset.into(), ); - let transaction_request = TransactionRequest::swap( + let transaction_request = TransactionRequestBuilder::swap( swap_transaction.clone(), (&self.note_type).into(), client.rng(), ) - .map_err(|err| err.to_string())?; + .map_err(|err| err.to_string())? + .build(); execute_transaction( &mut client, @@ -261,8 +265,8 @@ impl ConsumeNotesCmd { pub async fn execute(&self, mut client: Client) -> Result<(), String> { let force = self.force; - let mut list_of_authenticated_notes = Vec::new(); - let mut list_of_unauthenticated_notes = Vec::new(); + let mut authenticated_notes = Vec::new(); + let mut unauthenticated_notes = Vec::new(); for note_id in &self.list_of_notes { let note_record = get_input_note_with_id_prefix(&client, note_id) @@ -270,28 +274,29 @@ impl ConsumeNotesCmd { .map_err(|err| err.to_string())?; if note_record.is_authenticated() { - list_of_authenticated_notes.push(note_record.id()); + authenticated_notes.push(note_record.id()); } else { - list_of_unauthenticated_notes.push((note_record.try_into()?, None)); + unauthenticated_notes.push((note_record.try_into()?, None)); } } let account_id = get_input_acc_id_by_prefix_or_default(&client, self.account_id.clone()).await?; - if list_of_authenticated_notes.is_empty() { + if authenticated_notes.is_empty() { info!("No input note IDs provided, getting all notes consumable by {}", account_id); let consumable_notes = client.get_consumable_notes(Some(account_id)).await?; - list_of_authenticated_notes.extend(consumable_notes.iter().map(|(note, _)| note.id())); + authenticated_notes.extend(consumable_notes.iter().map(|(note, _)| note.id())); } - if list_of_authenticated_notes.is_empty() && list_of_unauthenticated_notes.is_empty() { + if authenticated_notes.is_empty() && unauthenticated_notes.is_empty() { return Err(format!("No input notes were provided and the store does not contain any notes consumable by {account_id}")); } - let transaction_request = TransactionRequest::consume_notes(list_of_authenticated_notes) - .with_unauthenticated_input_notes(list_of_unauthenticated_notes); + let transaction_request = TransactionRequestBuilder::consume_notes(authenticated_notes) + .with_unauthenticated_input_notes(unauthenticated_notes) + .build(); execute_transaction( &mut client, diff --git a/crates/rust-client/src/tests.rs b/crates/rust-client/src/tests.rs index 73b9fc9f6..ab048a8c1 100644 --- a/crates/rust-client/src/tests.rs +++ b/crates/rust-client/src/tests.rs @@ -24,7 +24,7 @@ use crate::{ mock::create_test_client, rpc::NodeRpcClient, store::{InputNoteRecord, NoteFilter, Store, StoreError}, - transactions::TransactionRequest, + transactions::TransactionRequestBuilder, ClientError, }; @@ -403,13 +403,14 @@ async fn test_mint_transaction() { client.sync_state().await.unwrap(); // Test submitting a mint transaction - let transaction_request = TransactionRequest::mint_fungible_asset( + let transaction_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet.id(), 5u64).unwrap(), AccountId::from_hex("0x168187d729b31a84").unwrap(), miden_objects::notes::NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); let transaction = client.new_transaction(faucet.id(), transaction_request).await.unwrap(); @@ -434,13 +435,14 @@ async fn test_get_output_notes() { .unwrap(); // Test submitting a mint transaction - let transaction_request = TransactionRequest::mint_fungible_asset( + let transaction_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet.id(), 5u64).unwrap(), AccountId::from_hex("0x0123456789abcdef").unwrap(), miden_objects::notes::NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); //Before executing transaction, there are no output notes assert!(client.get_output_notes(NoteFilter::All).await.unwrap().is_empty()); @@ -500,7 +502,7 @@ async fn test_transaction_request_expiration() { .await .unwrap(); - let transaction_request = TransactionRequest::mint_fungible_asset( + let transaction_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet.id(), 5u64).unwrap(), AccountId::from_hex("0x168187d729b31a84").unwrap(), miden_objects::notes::NoteType::Private, @@ -508,7 +510,8 @@ async fn test_transaction_request_expiration() { ) .unwrap() .with_expiration_delta(5) - .unwrap(); + .unwrap() + .build(); let transaction = client.new_transaction(faucet.id(), transaction_request).await.unwrap(); @@ -543,13 +546,14 @@ async fn test_import_processing_note_returns_error() { .unwrap(); // Test submitting a mint transaction - let transaction_request = TransactionRequest::mint_fungible_asset( + let transaction_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet.id(), 5u64).unwrap(), account.id(), miden_objects::notes::NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); let transaction = client.new_transaction(faucet.id(), transaction_request.clone()).await.unwrap(); @@ -558,8 +562,9 @@ async fn test_import_processing_note_returns_error() { let note_id = transaction_request.expected_output_notes().next().unwrap().id(); let note = client.get_input_note(note_id).await.unwrap(); - let input = [(note.try_into().unwrap(), None)].into_iter(); - let consume_note_request = TransactionRequest::new().with_unauthenticated_input_notes(input); + let input = [(note.try_into().unwrap(), None)]; + let consume_note_request = + TransactionRequestBuilder::new().with_unauthenticated_input_notes(input).build(); let transaction = client .new_transaction(account.id(), consume_note_request.clone()) .await @@ -605,7 +610,8 @@ async fn test_no_nonce_change_transaction_request() { let tx_script = client.compile_tx_script(vec![], code).unwrap(); - let transaction_request = TransactionRequest::new().with_custom_script(tx_script).unwrap(); + let transaction_request = + TransactionRequestBuilder::new().with_custom_script(tx_script).unwrap().build(); let transaction_execution_result = client.new_transaction(regular_account.id(), transaction_request).await.unwrap(); diff --git a/crates/rust-client/src/transactions/mod.rs b/crates/rust-client/src/transactions/mod.rs index 4b619f962..d29dd5ac3 100644 --- a/crates/rust-client/src/transactions/mod.rs +++ b/crates/rust-client/src/transactions/mod.rs @@ -42,7 +42,7 @@ use crate::{ mod request; pub use request::{ NoteArgs, PaymentTransactionData, SwapTransactionData, TransactionRequest, - TransactionRequestError, TransactionScriptTemplate, + TransactionRequestBuilder, TransactionRequestError, TransactionScriptTemplate, }; mod script_builder; @@ -922,8 +922,8 @@ mod test { Felt, FieldElement, Word, }; - use super::{PaymentTransactionData, TransactionRequest}; - use crate::mock::create_test_client; + use super::PaymentTransactionData; + use crate::{mock::create_test_client, transactions::TransactionRequestBuilder}; #[tokio::test] async fn test_transaction_creates_two_notes() { @@ -965,7 +965,7 @@ mod test { .await .unwrap(); client.sync_state().await.unwrap(); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new( vec![asset_1, asset_2], account.id(), @@ -975,7 +975,8 @@ mod test { NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); let tx_result = client.new_transaction(account.id(), tx_request).await.unwrap(); assert!(tx_result diff --git a/crates/rust-client/src/transactions/request.rs b/crates/rust-client/src/transactions/request/builder.rs similarity index 51% rename from crates/rust-client/src/transactions/request.rs rename to crates/rust-client/src/transactions/request/builder.rs index d62e229e1..932c97d81 100644 --- a/crates/rust-client/src/transactions/request.rs +++ b/crates/rust-client/src/transactions/request/builder.rs @@ -1,65 +1,39 @@ //! Contains structures and functions related to transaction creation. - use alloc::{ collections::{BTreeMap, BTreeSet}, - string::{String, ToString}, + string::ToString, vec::Vec, }; -use core::fmt; use miden_lib::notes::{create_p2id_note, create_p2idr_note, create_swap_note}; use miden_objects::{ accounts::AccountId, - assembly::AssemblyError, assets::{Asset, FungibleAsset}, crypto::{ merkle::{InnerNodeInfo, MerkleStore}, rand::FeltRng, }, - notes::{Note, NoteDetails, NoteExecutionMode, NoteId, NoteTag, NoteType, PartialNote}, - transaction::{OutputNote, TransactionArgs, TransactionScript}, + notes::{Note, NoteDetails, NoteExecutionMode, NoteId, NoteTag, NoteType}, + transaction::{OutputNote, TransactionScript}, vm::AdviceMap, - Digest, Felt, FieldElement, NoteError, Word, + Digest, Felt, FieldElement, }; -use miden_tx::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; -use super::{ - script_builder::{AccountCapabilities, TransactionScriptBuilder}, - TransactionScriptBuilderError, -}; +use super::{NoteArgs, TransactionRequest, TransactionRequestError, TransactionScriptTemplate}; -// TRANSACTION REQUEST +// TRANSACTION REQUEST BUILDER // ================================================================================================ -pub type NoteArgs = Word; - -/// Specifies a transaction script to be executed in a transaction. -/// -/// A transaction script is a program which is executed after scripts of all input notes have been -/// executed. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TransactionScriptTemplate { - /// Specifies the exact transaction script to be executed in a transaction. - CustomScript(TransactionScript), - /// Specifies that the transaction script must create the specified output notes. - /// - /// It is up to the client to determine how the output notes will be created and this will - /// depend on the capabilities of the account the transaction request will be applied to. - /// For example, for Basic Wallets, this may involve invoking `create_note` procedure. - SendNotes(Vec), -} - -/// A request for a transaction that can be executed by an account. +/// A builder for a [TransactionRequest]. /// -/// A request contains information about input notes to be consumed by the transaction (if any), -/// description of the transaction script to be executed (if any), and a set of notes expected -/// to be generated by the transaction or by consuming notes generated by the transaction. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TransactionRequest { - // Notes to be consumed by the transaction that are not authenticated. +/// Use this builder to construct a [TransactionRequest] by adding input notes, specifying scripts, +/// and setting other transaction parameters. +#[derive(Clone, Debug)] +pub struct TransactionRequestBuilder { + /// Notes to be consumed by the transaction that are not authenticated. unauthenticated_input_notes: Vec, /// Notes to be consumed by the transaction together with their (optional) arguments. This - /// has to include both authenticated and unauthenticated notes. + /// includes both authenticated and unauthenticated notes. input_notes: BTreeMap>, /// Template for the creation of the transaction script. script_template: Option, @@ -83,11 +57,11 @@ pub struct TransactionRequest { expiration_delta: Option, } -impl TransactionRequest { +impl TransactionRequestBuilder { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new, empty [TransactionRequest]. + /// Creates a new, empty [TransactionRequestBuilder]. pub fn new() -> Self { Self { unauthenticated_input_notes: vec![], @@ -126,7 +100,7 @@ impl TransactionRequest { } /// Specifies the output notes that should be created in the transaction script and will - /// be used as a transaction script template. These notes will also be added the the expected + /// be used as a transaction script template. These notes will also be added to the expected /// output notes of the transaction. /// /// If a transaction script template is already set (e.g. by calling `with_custom_script`), this @@ -223,7 +197,7 @@ impl TransactionRequest { self } - /// Extends the advice map with the specified ([Word], Vec<[Felt]>) pairs. + /// Extends the advice map with the specified ([Digest], Vec<[Felt]>) pairs. pub fn extend_advice_map)>>( mut self, iter: T, @@ -261,7 +235,7 @@ impl TransactionRequest { // STANDARDIZED REQUESTS // -------------------------------------------------------------------------------------------- - /// Returns a new [TransactionRequest] for a transaction to consume the specified notes. + /// Returns a new [TransactionRequestBuilder] for a transaction to consume the specified notes. /// /// - `note_ids` is a list of note IDs to be consumed. pub fn consume_notes(note_ids: Vec) -> Self { @@ -269,8 +243,8 @@ impl TransactionRequest { Self::new().with_authenticated_input_notes(input_notes) } - /// Returns a new [TransactionRequest] for a transaction to mint fungible assets. This request - /// must be executed against a fungible faucet account. + /// Returns a new [TransactionRequestBuilder] for a transaction to mint fungible assets. This + /// request must be executed against a fungible faucet account. /// /// - `asset` is the fungible asset to be minted. /// - `target_id` is the account ID of the account to receive the minted asset. @@ -292,11 +266,11 @@ impl TransactionRequest { rng, )?; - TransactionRequest::new().with_own_output_notes(vec![OutputNote::Full(created_note)]) + Self::new().with_own_output_notes(vec![OutputNote::Full(created_note)]) } - /// Returns a new [TransactionRequest] for a transaction to send a P2ID or P2IDR note. This - /// request must be executed against the wallet sender account. + /// Returns a new [TransactionRequestBuilder] for a transaction to send a P2ID or P2IDR note. + /// This request must be executed against the wallet sender account. /// /// - `payment_data` is the data for the payment transaction that contains the asset to be /// transferred, the sender account ID, and the target account ID. @@ -337,11 +311,11 @@ impl TransactionRequest { )? }; - TransactionRequest::new().with_own_output_notes(vec![OutputNote::Full(created_note)]) + Self::new().with_own_output_notes(vec![OutputNote::Full(created_note)]) } - /// Returns a new [TransactionRequest] for a transaction to send a SWAP note. This request must - /// be executed against the wallet sender account. + /// Returns a new [TransactionRequestBuilder] for a transaction to send a SWAP note. This + /// request must be executed against the wallet sender account. /// /// - `swap_data` is the data for the swap transaction that contains the sender account ID, the /// offered asset, and the requested asset. @@ -367,257 +341,30 @@ impl TransactionRequest { let payback_tag = NoteTag::from_account_id(swap_data.account_id(), NoteExecutionMode::Local)?; - TransactionRequest::new() + Self::new() .with_expected_future_notes(vec![(payback_note_details, payback_tag)]) .with_own_output_notes(vec![OutputNote::Full(created_note)]) } - // PUBLIC ACCESSORS + // FINALIZE BUILDER // -------------------------------------------------------------------------------------------- - /// Returns a reference to the transaction request's unauthenticated note list. - pub fn unauthenticated_input_notes(&self) -> &[Note] { - &self.unauthenticated_input_notes - } - - /// Returns an iterator over unauthenticated note IDs for the transaction request. - pub fn unauthenticated_input_note_ids(&self) -> impl Iterator + '_ { - self.unauthenticated_input_notes.iter().map(|note| note.id()) - } - - /// Returns an iterator over authenticated input note IDs for the transaction request. - pub fn authenticated_input_note_ids(&self) -> impl Iterator + '_ { - let unauthenticated_note_ids: BTreeSet = - BTreeSet::from_iter(self.unauthenticated_input_note_ids()); - - self.input_notes() - .iter() - .map(|(note_id, _)| *note_id) - .filter(move |note_id| !unauthenticated_note_ids.contains(note_id)) - } - - /// Returns a mapping for input note IDs and their optional [NoteArgs]. - pub fn input_notes(&self) -> &BTreeMap> { - &self.input_notes - } - - /// Returns a list of all input note IDs. - pub fn get_input_note_ids(&self) -> Vec { - self.input_notes.keys().cloned().collect() - } - - /// Returns a map of note IDs to their respective [NoteArgs]. The result will include - /// exclusively note IDs for notes for which [NoteArgs] have been defined. - pub fn get_note_args(&self) -> BTreeMap { - self.input_notes - .iter() - .filter_map(|(note, args)| args.map(|a| (*note, a))) - .collect() - } - - /// Returns an iterator over the expected output notes. - pub fn expected_output_notes(&self) -> impl Iterator { - self.expected_output_notes.values() - } - - /// Returns an iterator over expected future notes. - pub fn expected_future_notes(&self) -> impl Iterator { - self.expected_future_notes.values() - } - - /// Returns the [TransactionScriptTemplate]. - pub fn script_template(&self) -> &Option { - &self.script_template - } - - /// Returns the [AdviceMap] for the transaction request. - pub fn advice_map(&self) -> &AdviceMap { - &self.advice_map - } - - /// Returns the [MerkleStore] for the transaction request. - pub fn merkle_store(&self) -> &MerkleStore { - &self.merkle_store - } - - /// Returns the IDs of the required foreign accounts for the transaction request. - pub fn foreign_accounts(&self) -> &BTreeSet { - &self.foreign_account_ids - } - - /// Converts the [TransactionRequest] into [TransactionArgs] in order to be executed by a Miden - /// host. - pub(super) fn into_transaction_args(self, tx_script: TransactionScript) -> TransactionArgs { - let note_args = self.get_note_args(); - let TransactionRequest { - expected_output_notes, - advice_map, - merkle_store, - .. - } = self; - - let mut tx_args = TransactionArgs::new(Some(tx_script), note_args.into(), advice_map); - - tx_args.extend_expected_output_notes(expected_output_notes.into_values()); - tx_args.extend_merkle_store(merkle_store.inner_nodes()); - - tx_args - } - - pub(crate) fn build_transaction_script( - &self, - account_capabilities: AccountCapabilities, - ) -> Result { - match &self.script_template { - Some(TransactionScriptTemplate::CustomScript(script)) => Ok(script.clone()), - Some(TransactionScriptTemplate::SendNotes(notes)) => { - let tx_script_builder = - TransactionScriptBuilder::new(account_capabilities, self.expiration_delta); - - Ok(tx_script_builder.build_send_notes_script(notes)?) - }, - None => { - if self.input_notes.is_empty() { - Err(TransactionRequestError::NoInputNotes) - } else { - let tx_script_builder = - TransactionScriptBuilder::new(account_capabilities, self.expiration_delta); - - Ok(tx_script_builder.build_auth_script()?) - } - }, - } - } -} - -impl Serializable for TransactionRequest { - fn write_into(&self, target: &mut W) { - self.unauthenticated_input_notes.write_into(target); - self.input_notes.write_into(target); - match &self.script_template { - None => target.write_u8(0), - Some(TransactionScriptTemplate::CustomScript(script)) => { - target.write_u8(1); - script.write_into(target); - }, - Some(TransactionScriptTemplate::SendNotes(notes)) => { - target.write_u8(2); - notes.write_into(target); - }, + /// Consumes the builder and returns a [TransactionRequest]. + pub fn build(self) -> TransactionRequest { + TransactionRequest { + unauthenticated_input_notes: self.unauthenticated_input_notes, + input_notes: self.input_notes, + script_template: self.script_template, + expected_output_notes: self.expected_output_notes, + expected_future_notes: self.expected_future_notes, + advice_map: self.advice_map, + merkle_store: self.merkle_store, + foreign_account_ids: self.foreign_account_ids, + expiration_delta: self.expiration_delta, } - self.expected_output_notes.write_into(target); - self.expected_future_notes.write_into(target); - self.advice_map.clone().into_iter().collect::>().write_into(target); - self.merkle_store.write_into(target); - self.foreign_account_ids.write_into(target); - self.expiration_delta.write_into(target); - } -} - -impl Deserializable for TransactionRequest { - fn read_from(source: &mut R) -> Result { - let unauthenticated_input_notes = Vec::::read_from(source)?; - let input_notes = BTreeMap::>::read_from(source)?; - - let script_template = match source.read_u8()? { - 0 => None, - 1 => { - let transaction_script = TransactionScript::read_from(source)?; - Some(TransactionScriptTemplate::CustomScript(transaction_script)) - }, - 2 => { - let notes = Vec::::read_from(source)?; - Some(TransactionScriptTemplate::SendNotes(notes)) - }, - _ => { - return Err(DeserializationError::InvalidValue( - "Invalid script template type".to_string(), - )) - }, - }; - - let expected_output_notes = BTreeMap::::read_from(source)?; - let expected_future_notes = BTreeMap::::read_from(source)?; - - let mut advice_map = AdviceMap::new(); - let advice_vec = Vec::<(Digest, Vec)>::read_from(source)?; - advice_map.extend(advice_vec); - let merkle_store = MerkleStore::read_from(source)?; - let foreign_account_ids = BTreeSet::::read_from(source)?; - let expiration_delta = Option::::read_from(source)?; - - Ok(TransactionRequest { - unauthenticated_input_notes, - input_notes, - script_template, - expected_output_notes, - expected_future_notes, - advice_map, - merkle_store, - foreign_account_ids, - expiration_delta, - }) - } -} - -impl Default for TransactionRequest { - fn default() -> Self { - Self::new() } } -// TRANSACTION REQUEST ERROR -// ================================================================================================ - -/// Errors related to a [TransactionRequest] -#[derive(Debug)] -pub enum TransactionRequestError { - InvalidForeignAccountId(AccountId), - InputNoteNotAuthenticated, - InputNotesMapMissingUnauthenticatedNotes, - InvalidNoteVariant, - InvalidSenderAccount(AccountId), - InvalidTransactionScript(AssemblyError), - NoInputNotes, - ScriptTemplateError(String), - NoteNotFound(String), - NoteCreationError(NoteError), - TransactionScriptBuilderError(TransactionScriptBuilderError), -} - -impl From for TransactionRequestError { - fn from(err: TransactionScriptBuilderError) -> Self { - Self::TransactionScriptBuilderError(err) - } -} - -impl fmt::Display for TransactionRequestError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::InvalidForeignAccountId(acc_id) => write!(f, "Requested foreign account with ID {acc_id} is not public"), - Self::InputNoteNotAuthenticated => write!(f, "Every authenticated note to be consumed should be committed and contain a valid inclusion proof"), - Self::InputNotesMapMissingUnauthenticatedNotes => write!(f, "The input notes map should include keys for all provided unauthenticated input notes"), - Self::InvalidNoteVariant => write!(f, "Own notes should be either full or partial, but not header"), - Self::InvalidSenderAccount(account_id) => write!(f, "Invalid sender account ID: {}", account_id), - Self::InvalidTransactionScript(err) => write!(f, "Invalid transaction script: {}", err), - Self::NoInputNotes => write!(f, "A transaction without output notes must have at least one input note"), - Self::ScriptTemplateError(err) => write!(f, "Transaction script template error: {}", err), - Self::NoteNotFound(err) => write!(f, "Note not found: {}", err), - Self::NoteCreationError(err) => write!(f, "Note creation error: {}", err), - Self::TransactionScriptBuilderError(err) => write!(f, "Transaction script builder error: {}", err), - } - } -} - -impl From for TransactionRequestError { - fn from(err: NoteError) -> Self { - Self::NoteCreationError(err) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for TransactionRequestError {} - // PAYMENT TRANSACTION DATA // ================================================================================================ @@ -715,75 +462,3 @@ impl SwapTransactionData { self.requested_asset } } - -// TESTS -// ================================================================================================ - -#[cfg(test)] -mod tests { - use std::vec::Vec; - - use miden_lib::notes::create_p2id_note; - use miden_objects::{ - accounts::{AccountId, AccountType}, - assets::FungibleAsset, - crypto::rand::{FeltRng, RpoRandomCoin}, - notes::{NoteExecutionMode, NoteTag, NoteType}, - transaction::OutputNote, - Digest, Felt, ZERO, - }; - use miden_tx::utils::{Deserializable, Serializable}; - - use super::TransactionRequest; - - #[test] - fn transaction_request_serialization() { - let sender_id = AccountId::new_dummy([0u8; 32], AccountType::RegularAccountImmutableCode); - let target_id = AccountId::new_dummy([1u8; 32], AccountType::RegularAccountImmutableCode); - let faucet_id = AccountId::new_dummy([2u8; 32], AccountType::FungibleFaucet); - let mut rng = RpoRandomCoin::new(Default::default()); - - let mut notes = vec![]; - for i in 0..6 { - let note = create_p2id_note( - sender_id, - target_id, - vec![FungibleAsset::new(faucet_id, 100 + i).unwrap().into()], - NoteType::Private, - ZERO, - &mut rng, - ) - .unwrap(); - notes.push(note); - } - - let mut advice_vec: Vec<(Digest, Vec)> = vec![]; - for i in 0..10 { - advice_vec.push((Digest::new(rng.draw_word()), vec![Felt::new(i)])); - } - - // This transaction request wouldn't be valid in a real scenario, it's intended for testing - let tx_request = TransactionRequest::new() - .with_authenticated_input_notes(vec![(notes.pop().unwrap().id(), None)]) - .with_unauthenticated_input_notes(vec![(notes.pop().unwrap(), None)]) - .with_expected_output_notes(vec![notes.pop().unwrap()]) - .with_expected_future_notes(vec![( - notes.pop().unwrap().into(), - NoteTag::from_account_id(sender_id, NoteExecutionMode::Local).unwrap(), - )]) - .extend_advice_map(advice_vec) - .with_public_foreign_accounts([target_id]) - .unwrap() - .with_own_output_notes(vec![ - OutputNote::Full(notes.pop().unwrap()), - OutputNote::Partial(notes.pop().unwrap().into()), - ]) - .unwrap(); - - let mut buffer = Vec::new(); - tx_request.write_into(&mut buffer); - - let deserialized_tx_request = TransactionRequest::read_from_bytes(&buffer).unwrap(); - assert_eq!(tx_request, deserialized_tx_request); - } -} diff --git a/crates/rust-client/src/transactions/request/mod.rs b/crates/rust-client/src/transactions/request/mod.rs new file mode 100644 index 000000000..ac85ef20a --- /dev/null +++ b/crates/rust-client/src/transactions/request/mod.rs @@ -0,0 +1,405 @@ +//! Contains structures and functions related to transaction creation. + +use alloc::{ + collections::{BTreeMap, BTreeSet}, + string::{String, ToString}, + vec::Vec, +}; +use core::fmt; + +use miden_objects::{ + accounts::AccountId, + assembly::AssemblyError, + crypto::merkle::MerkleStore, + notes::{Note, NoteDetails, NoteId, NoteTag, PartialNote}, + transaction::{TransactionArgs, TransactionScript}, + vm::AdviceMap, + Digest, Felt, NoteError, Word, +}; +use miden_tx::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; + +use super::{ + script_builder::{AccountCapabilities, TransactionScriptBuilder}, + TransactionScriptBuilderError, +}; + +mod builder; +pub use builder::{PaymentTransactionData, SwapTransactionData, TransactionRequestBuilder}; + +// TRANSACTION REQUEST +// ================================================================================================ + +pub type NoteArgs = Word; + +/// Specifies a transaction script to be executed in a transaction. +/// +/// A transaction script is a program which is executed after scripts of all input notes have been +/// executed. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum TransactionScriptTemplate { + /// Specifies the exact transaction script to be executed in a transaction. + CustomScript(TransactionScript), + /// Specifies that the transaction script must create the specified output notes. + /// + /// It is up to the client to determine how the output notes will be created and this will + /// depend on the capabilities of the account the transaction request will be applied to. + /// For example, for Basic Wallets, this may involve invoking `create_note` procedure. + SendNotes(Vec), +} + +/// Specifies a transaction request that can be executed by an account. +/// +/// A request contains information about input notes to be consumed by the transaction (if any), +/// description of the transaction script to be executed (if any), and a set of notes expected +/// to be generated by the transaction or by consuming notes generated by the transaction. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TransactionRequest { + /// Notes to be consumed by the transaction that are not authenticated. + unauthenticated_input_notes: Vec, + /// Notes to be consumed by the transaction together with their (optional) arguments. This + /// includes both authenticated and unauthenticated notes. + input_notes: BTreeMap>, + /// Template for the creation of the transaction script. + script_template: Option, + /// A map of notes expected to be generated by the transactions. + expected_output_notes: BTreeMap, + /// A map of details and tags of notes we expect to be created as part of future transactions + /// with their respective tags. + /// + /// For example, after a swap note is consumed, a payback note is expected to be created. + expected_future_notes: BTreeMap, + /// Initial state of the `AdviceMap` that provides data during runtime. + advice_map: AdviceMap, + /// Initial state of the `MerkleStore` that provides data during runtime. + merkle_store: MerkleStore, + /// Foreign account data requirements. At execution time, account state will be retrieved from + /// the network, and injected as advice inputs. Additionally, the account's code will be + /// added to the executor and prover. + foreign_account_ids: BTreeSet, + /// The number of blocks in relation to the transaction's reference block after which the + /// transaction will expire. + expiration_delta: Option, +} + +impl TransactionRequest { + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns a reference to the transaction request's unauthenticated note list. + pub fn unauthenticated_input_notes(&self) -> &[Note] { + &self.unauthenticated_input_notes + } + + /// Returns an iterator over unauthenticated note IDs for the transaction request. + pub fn unauthenticated_input_note_ids(&self) -> impl Iterator + '_ { + self.unauthenticated_input_notes.iter().map(|note| note.id()) + } + + /// Returns an iterator over authenticated input note IDs for the transaction request. + pub fn authenticated_input_note_ids(&self) -> impl Iterator + '_ { + let unauthenticated_note_ids: BTreeSet = + BTreeSet::from_iter(self.unauthenticated_input_note_ids()); + + self.input_notes() + .iter() + .map(|(note_id, _)| *note_id) + .filter(move |note_id| !unauthenticated_note_ids.contains(note_id)) + } + + /// Returns a mapping for input note IDs and their optional [NoteArgs]. + pub fn input_notes(&self) -> &BTreeMap> { + &self.input_notes + } + + /// Returns a list of all input note IDs. + pub fn get_input_note_ids(&self) -> Vec { + self.input_notes.keys().cloned().collect() + } + + /// Returns a map of note IDs to their respective [NoteArgs]. The result will include + /// exclusively note IDs for notes for which [NoteArgs] have been defined. + pub fn get_note_args(&self) -> BTreeMap { + self.input_notes + .iter() + .filter_map(|(note, args)| args.map(|a| (*note, a))) + .collect() + } + + /// Returns an iterator over the expected output notes. + pub fn expected_output_notes(&self) -> impl Iterator { + self.expected_output_notes.values() + } + + /// Returns an iterator over expected future notes. + pub fn expected_future_notes(&self) -> impl Iterator { + self.expected_future_notes.values() + } + + /// Returns the [TransactionScriptTemplate]. + pub fn script_template(&self) -> &Option { + &self.script_template + } + + /// Returns the [AdviceMap] for the transaction request. + pub fn advice_map(&self) -> &AdviceMap { + &self.advice_map + } + + /// Returns the [MerkleStore] for the transaction request. + pub fn merkle_store(&self) -> &MerkleStore { + &self.merkle_store + } + + /// Returns the IDs of the required foreign accounts for the transaction request. + pub fn foreign_accounts(&self) -> &BTreeSet { + &self.foreign_account_ids + } + + /// Converts the [TransactionRequest] into [TransactionArgs] in order to be executed by a Miden + /// host. + pub(super) fn into_transaction_args(self, tx_script: TransactionScript) -> TransactionArgs { + let note_args = self.get_note_args(); + let TransactionRequest { + expected_output_notes, + advice_map, + merkle_store, + .. + } = self; + + let mut tx_args = TransactionArgs::new(Some(tx_script), note_args.into(), advice_map); + + tx_args.extend_expected_output_notes(expected_output_notes.into_values()); + tx_args.extend_merkle_store(merkle_store.inner_nodes()); + + tx_args + } + + pub(crate) fn build_transaction_script( + &self, + account_capabilities: AccountCapabilities, + ) -> Result { + match &self.script_template { + Some(TransactionScriptTemplate::CustomScript(script)) => Ok(script.clone()), + Some(TransactionScriptTemplate::SendNotes(notes)) => { + let tx_script_builder = + TransactionScriptBuilder::new(account_capabilities, self.expiration_delta); + + Ok(tx_script_builder.build_send_notes_script(notes)?) + }, + None => { + if self.input_notes.is_empty() { + Err(TransactionRequestError::NoInputNotes) + } else { + let tx_script_builder = + TransactionScriptBuilder::new(account_capabilities, self.expiration_delta); + + Ok(tx_script_builder.build_auth_script()?) + } + }, + } + } +} + +impl Serializable for TransactionRequest { + fn write_into(&self, target: &mut W) { + self.unauthenticated_input_notes.write_into(target); + self.input_notes.write_into(target); + match &self.script_template { + None => target.write_u8(0), + Some(TransactionScriptTemplate::CustomScript(script)) => { + target.write_u8(1); + script.write_into(target); + }, + Some(TransactionScriptTemplate::SendNotes(notes)) => { + target.write_u8(2); + notes.write_into(target); + }, + } + self.expected_output_notes.write_into(target); + self.expected_future_notes.write_into(target); + self.advice_map.clone().into_iter().collect::>().write_into(target); + self.merkle_store.write_into(target); + self.foreign_account_ids.write_into(target); + self.expiration_delta.write_into(target); + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Deserializable for TransactionRequest { + fn read_from(source: &mut R) -> Result { + let unauthenticated_input_notes = Vec::::read_from(source)?; + let input_notes = BTreeMap::>::read_from(source)?; + + let script_template = match source.read_u8()? { + 0 => None, + 1 => { + let transaction_script = TransactionScript::read_from(source)?; + Some(TransactionScriptTemplate::CustomScript(transaction_script)) + }, + 2 => { + let notes = Vec::::read_from(source)?; + Some(TransactionScriptTemplate::SendNotes(notes)) + }, + _ => { + return Err(DeserializationError::InvalidValue( + "Invalid script template type".to_string(), + )) + }, + }; + + let expected_output_notes = BTreeMap::::read_from(source)?; + let expected_future_notes = BTreeMap::::read_from(source)?; + + let mut advice_map = AdviceMap::new(); + let advice_vec = Vec::<(Digest, Vec)>::read_from(source)?; + advice_map.extend(advice_vec); + let merkle_store = MerkleStore::read_from(source)?; + let foreign_account_ids = BTreeSet::::read_from(source)?; + let expiration_delta = Option::::read_from(source)?; + + Ok(TransactionRequest { + unauthenticated_input_notes, + input_notes, + script_template, + expected_output_notes, + expected_future_notes, + advice_map, + merkle_store, + foreign_account_ids, + expiration_delta, + }) + } +} + +impl Default for TransactionRequestBuilder { + fn default() -> Self { + Self::new() + } +} + +// TRANSACTION REQUEST ERROR +// ================================================================================================ + +/// Errors related to a [TransactionRequest] +#[derive(Debug)] +pub enum TransactionRequestError { + InvalidForeignAccountId(AccountId), + InputNoteNotAuthenticated, + InputNotesMapMissingUnauthenticatedNotes, + InvalidNoteVariant, + InvalidSenderAccount(AccountId), + InvalidTransactionScript(AssemblyError), + NoInputNotes, + ScriptTemplateError(String), + NoteNotFound(String), + NoteCreationError(NoteError), + TransactionScriptBuilderError(TransactionScriptBuilderError), +} + +impl From for TransactionRequestError { + fn from(err: TransactionScriptBuilderError) -> Self { + Self::TransactionScriptBuilderError(err) + } +} + +impl fmt::Display for TransactionRequestError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidForeignAccountId(acc_id) => write!(f, "Requested foreign account with ID {acc_id} is not public"), + Self::InputNoteNotAuthenticated => write!(f, "Every authenticated note to be consumed should be committed and contain a valid inclusion proof"), + Self::InputNotesMapMissingUnauthenticatedNotes => write!(f, "The input notes map should include keys for all provided unauthenticated input notes"), + Self::InvalidNoteVariant => write!(f, "Own notes should be either full or partial, but not header"), + Self::InvalidSenderAccount(account_id) => write!(f, "Invalid sender account ID: {}", account_id), + Self::InvalidTransactionScript(err) => write!(f, "Invalid transaction script: {}", err), + Self::NoInputNotes => write!(f, "A transaction without output notes must have at least one input note"), + Self::ScriptTemplateError(err) => write!(f, "Transaction script template error: {}", err), + Self::NoteNotFound(err) => write!(f, "Note not found: {}", err), + Self::NoteCreationError(err) => write!(f, "Note creation error: {}", err), + Self::TransactionScriptBuilderError(err) => write!(f, "Transaction script builder error: {}", err), + } + } +} + +impl From for TransactionRequestError { + fn from(err: NoteError) -> Self { + Self::NoteCreationError(err) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TransactionRequestError {} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use std::vec::Vec; + + use miden_lib::notes::create_p2id_note; + use miden_objects::{ + accounts::{AccountId, AccountType}, + assets::FungibleAsset, + crypto::rand::{FeltRng, RpoRandomCoin}, + notes::{NoteExecutionMode, NoteTag, NoteType}, + transaction::OutputNote, + Digest, Felt, ZERO, + }; + use miden_tx::utils::{Deserializable, Serializable}; + + use super::{TransactionRequest, TransactionRequestBuilder}; + + #[test] + fn transaction_request_serialization() { + let sender_id = AccountId::new_dummy([0u8; 32], AccountType::RegularAccountImmutableCode); + let target_id = AccountId::new_dummy([1u8; 32], AccountType::RegularAccountImmutableCode); + let faucet_id = AccountId::new_dummy([2u8; 32], AccountType::FungibleFaucet); + let mut rng = RpoRandomCoin::new(Default::default()); + + let mut notes = vec![]; + for i in 0..6 { + let note = create_p2id_note( + sender_id, + target_id, + vec![FungibleAsset::new(faucet_id, 100 + i).unwrap().into()], + NoteType::Private, + ZERO, + &mut rng, + ) + .unwrap(); + notes.push(note); + } + + let mut advice_vec: Vec<(Digest, Vec)> = vec![]; + for i in 0..10 { + advice_vec.push((Digest::new(rng.draw_word()), vec![Felt::new(i)])); + } + + // This transaction request wouldn't be valid in a real scenario, it's intended for testing + let tx_request = TransactionRequestBuilder::new() + .with_authenticated_input_notes(vec![(notes.pop().unwrap().id(), None)]) + .with_unauthenticated_input_notes(vec![(notes.pop().unwrap(), None)]) + .with_expected_output_notes(vec![notes.pop().unwrap()]) + .with_expected_future_notes(vec![( + notes.pop().unwrap().into(), + NoteTag::from_account_id(sender_id, NoteExecutionMode::Local).unwrap(), + )]) + .extend_advice_map(advice_vec) + .with_public_foreign_accounts([target_id]) + .unwrap() + .with_own_output_notes(vec![ + OutputNote::Full(notes.pop().unwrap()), + OutputNote::Partial(notes.pop().unwrap().into()), + ]) + .unwrap() + .build(); + + let mut buffer = Vec::new(); + tx_request.write_into(&mut buffer); + + let deserialized_tx_request = TransactionRequest::read_from_bytes(&buffer).unwrap(); + assert_eq!(tx_request, deserialized_tx_request); + } +} diff --git a/crates/web-client/js/index.js b/crates/web-client/js/index.js index 88c885a25..90fbff486 100644 --- a/crates/web-client/js/index.js +++ b/crates/web-client/js/index.js @@ -33,6 +33,7 @@ const { TestUtils, TransactionFilter, TransactionRequest, + TransactionRequestBuilder, TransactionScriptInputPair, TransactionScriptInputPairArray, WebClient, @@ -75,6 +76,7 @@ export { TestUtils, TransactionFilter, TransactionRequest, + TransactionRequestBuilder, TransactionScriptInputPair, TransactionScriptInputPairArray, WebClient, diff --git a/crates/web-client/js/types/index.d.ts b/crates/web-client/js/types/index.d.ts index 87fadd4b4..75a806b9c 100644 --- a/crates/web-client/js/types/index.d.ts +++ b/crates/web-client/js/types/index.d.ts @@ -34,6 +34,7 @@ export { TestUtils, TransactionFilter, TransactionRequest, + TransactionRequestBuilder, TransactionScriptInputPair, TransactionScriptInputPairArray, WebClient, diff --git a/crates/web-client/src/models/transaction_request.rs b/crates/web-client/src/models/transaction_request.rs index a2b9e52d9..c50c3113f 100644 --- a/crates/web-client/src/models/transaction_request.rs +++ b/crates/web-client/src/models/transaction_request.rs @@ -1,5 +1,6 @@ use miden_client::transactions::{ NoteArgs as NativeNoteArgs, TransactionRequest as NativeTransactionRequest, + TransactionRequestBuilder as NativeTransactionRequestBuilder, }; use miden_objects::{ notes::{ @@ -235,17 +236,22 @@ impl From<&NoteDetailsAndTagArray> for Vec<(NativeNoteDetails, NativeNoteTag)> { } } +// Transaction Request Builder +#[derive(Clone)] +#[wasm_bindgen] +pub struct TransactionRequestBuilder(NativeTransactionRequestBuilder); + // Transaction Request #[derive(Clone)] #[wasm_bindgen] pub struct TransactionRequest(NativeTransactionRequest); #[wasm_bindgen] -impl TransactionRequest { +impl TransactionRequestBuilder { #[wasm_bindgen(constructor)] - pub fn new() -> TransactionRequest { - let native_transaction_request = NativeTransactionRequest::new(); - TransactionRequest(native_transaction_request) + pub fn new() -> TransactionRequestBuilder { + let native_transaction_request = NativeTransactionRequestBuilder::new(); + TransactionRequestBuilder(native_transaction_request) } pub fn with_unauthenticated_input_notes(mut self, notes: &NoteAndArgsArray) -> Self { @@ -294,25 +300,41 @@ impl TransactionRequest { self.0 = self.0.clone().extend_advice_map(native_advice_map); self } + + pub fn build(self) -> TransactionRequest { + TransactionRequest(self.0.build()) + } } // CONVERSIONS // ================================================================================================ -impl From for NativeTransactionRequest { - fn from(transaction_request: TransactionRequest) -> Self { +impl From for NativeTransactionRequestBuilder { + fn from(transaction_request: TransactionRequestBuilder) -> Self { transaction_request.0 } } -impl From<&TransactionRequest> for NativeTransactionRequest { - fn from(transaction_request: &TransactionRequest) -> Self { +impl From<&TransactionRequestBuilder> for NativeTransactionRequestBuilder { + fn from(transaction_request: &TransactionRequestBuilder) -> Self { transaction_request.0.clone() } } -impl Default for TransactionRequest { +impl Default for TransactionRequestBuilder { fn default() -> Self { Self::new() } } + +impl From for NativeTransactionRequest { + fn from(transaction_request: TransactionRequest) -> Self { + transaction_request.0 + } +} + +impl From<&TransactionRequest> for NativeTransactionRequest { + fn from(transaction_request: &TransactionRequest) -> Self { + transaction_request.0.clone() + } +} diff --git a/crates/web-client/src/new_transactions.rs b/crates/web-client/src/new_transactions.rs index 0e0da7f00..2bd3c3b45 100644 --- a/crates/web-client/src/new_transactions.rs +++ b/crates/web-client/src/new_transactions.rs @@ -2,7 +2,7 @@ use miden_client::{ notes::get_input_note_with_id_prefix, transactions::{ PaymentTransactionData, SwapTransactionData, - TransactionRequest as NativeTransactionRequest, + TransactionRequestBuilder as NativeTransactionRequestBuilder, TransactionResult as NativeTransactionResult, }, }; @@ -83,7 +83,7 @@ impl WebClient { JsValue::from_str(&format!("Failed to create Fungible Asset: {}", err)) })?; - let mint_transaction_request = NativeTransactionRequest::mint_fungible_asset( + let mint_transaction_request = NativeTransactionRequestBuilder::mint_fungible_asset( fungible_asset, target_account_id.into(), note_type.into(), @@ -91,7 +91,8 @@ impl WebClient { ) .map_err(|err| { JsValue::from_str(&format!("Failed to create Mint Transaction Request: {}", err)) - })?; + })? + .build(); let mint_transaction_execution_result = client .new_transaction(faucet_id.into(), mint_transaction_request) @@ -136,7 +137,7 @@ impl WebClient { ); let send_transaction_request = if let Some(recall_height) = recall_height { - NativeTransactionRequest::pay_to_id( + NativeTransactionRequestBuilder::pay_to_id( payment_transaction, Some(recall_height), note_type.into(), @@ -148,8 +149,9 @@ impl WebClient { err )) })? + .build() } else { - NativeTransactionRequest::pay_to_id( + NativeTransactionRequestBuilder::pay_to_id( payment_transaction, None, note_type.into(), @@ -161,6 +163,7 @@ impl WebClient { err )) })? + .build() }; let send_transaction_execution_result = client @@ -200,7 +203,8 @@ impl WebClient { result.push(note_record.id()); } - let consume_transaction_request = NativeTransactionRequest::consume_notes(result); + let consume_transaction_request = + NativeTransactionRequestBuilder::consume_notes(result).build(); let consume_transaction_execution_result = client .new_transaction(account_id.into(), consume_transaction_request) @@ -257,12 +261,13 @@ impl WebClient { requested_fungible_asset, ); - let swap_transaction_request = NativeTransactionRequest::swap( + let swap_transaction_request = NativeTransactionRequestBuilder::swap( swap_transaction.clone(), note_type.into(), client.rng(), ) - .unwrap(); + .unwrap() + .build(); let swap_transaction_execution_result = client .new_transaction(sender_account_id, swap_transaction_request.clone()) .await diff --git a/crates/web-client/test.html b/crates/web-client/test.html index 0ed59a620..166a32ad4 100644 --- a/crates/web-client/test.html +++ b/crates/web-client/test.html @@ -41,6 +41,7 @@ OutputNotesArray, Rpo256, TransactionRequest, + TransactionRequestBuilder, TransactionScriptInputPair, TransactionScriptInputPairArray, WebClient, @@ -1410,10 +1411,9 @@ let note = new Note(noteAssets, noteMetadata, noteRecipient); // Creating First Custom Transaction Request to Mint the Custom Note - let transaction_request = - new TransactionRequest().with_own_output_notes( - new OutputNotesArray([OutputNote.full(note)]) - ); + let transaction_request = new TransactionRequestBuilder() + .with_own_output_notes(new OutputNotesArray([OutputNote.full(note)])) + .build(); // Execute and Submit Transaction await webClient.fetch_and_cache_account_auth_by_pub_key( diff --git a/crates/web-client/test/global.test.d.ts b/crates/web-client/test/global.test.d.ts index 29397c67a..d675dd439 100644 --- a/crates/web-client/test/global.test.d.ts +++ b/crates/web-client/test/global.test.d.ts @@ -30,6 +30,7 @@ import { TestUtils, TransactionFilter, TransactionRequest, + TransactionRequestBuilder, TransactionScriptInputPair, TransactionScriptInputPairArray, WebClient, @@ -68,6 +69,7 @@ declare global { TestUtils: typeof TestUtils; TransactionFilter: typeof TransactionFilter; TransactionRequest: typeof TransactionRequest; + TransactionRequestBuilder: typeof TransactionRequestBuilder; TransactionScriptInputPair: typeof TransactionScriptInputPair; TransactionScriptInputPairArray: typeof TransactionScriptInputPairArray; create_client: () => Promise; diff --git a/crates/web-client/test/mocha.global.setup.mjs b/crates/web-client/test/mocha.global.setup.mjs index c54102153..2d0e984fe 100644 --- a/crates/web-client/test/mocha.global.setup.mjs +++ b/crates/web-client/test/mocha.global.setup.mjs @@ -72,6 +72,7 @@ before(async () => { TestUtils, TransactionFilter, TransactionRequest, + TransactionRequestBuilder, TransactionScriptInputPair, TransactionScriptInputPairArray, WebClient, @@ -115,6 +116,7 @@ before(async () => { window.TestUtils = TestUtils; window.TransactionFilter = TransactionFilter; window.TransactionRequest = TransactionRequest; + window.TransactionRequestBuilder = TransactionRequestBuilder; window.TransactionScriptInputPair = TransactionScriptInputPair; window.TransactionScriptInputPairArray = TransactionScriptInputPairArray; diff --git a/crates/web-client/test/new_transactions.test.ts b/crates/web-client/test/new_transactions.test.ts index 8c5dba9cd..a9d67f379 100644 --- a/crates/web-client/test/new_transactions.test.ts +++ b/crates/web-client/test/new_transactions.test.ts @@ -321,10 +321,11 @@ export const customTransaction = async ( let note = new window.Note(noteAssets, noteMetadata, noteRecipient); // Creating First Custom Transaction Request to Mint the Custom Note - let transaction_request = - new window.TransactionRequest().with_own_output_notes( + let transaction_request = new window.TransactionRequestBuilder() + .with_own_output_notes( new window.OutputNotesArray([window.OutputNote.full(note)]) - ); + ) + .build(); // Execute and Submit Transaction await client.fetch_and_cache_account_auth_by_pub_key(faucetAccount.id()); @@ -379,10 +380,11 @@ export const customTransaction = async ( let note_args_commitment_2 = window.Rpo256.hash_elements(feltArray); advice_map.insert(note_args_commitment_2, feltArray); - let transaction_request_2 = new window.TransactionRequest() + let transaction_request_2 = new window.TransactionRequestBuilder() .with_authenticated_input_notes(note_id_and_args_array) .with_custom_script(transaction_script) - .extend_advice_map(advice_map); + .extend_advice_map(advice_map) + .build(); // Execute and Submit Transaction await client.fetch_and_cache_account_auth_by_pub_key(walletAccount.id()); diff --git a/docs/library.md b/docs/library.md index 3e5a18b09..04b836e99 100644 --- a/docs/library.md +++ b/docs/library.md @@ -95,7 +95,7 @@ let payment_transaction = PaymentTransactionData::new( target_account_id, ); -let transaction_request = TransactionRequest::pay_to_id( +let transaction_request = TransactionRequestBuilder::pay_to_id( payment_transaction, None, NoteType::Private, diff --git a/tests/integration/common.rs b/tests/integration/common.rs index 8ed4f905d..c2f6a1d90 100644 --- a/tests/integration/common.rs +++ b/tests/integration/common.rs @@ -7,7 +7,9 @@ use miden_client::{ rpc::{Endpoint, RpcError, TonicRpcClient}, store::{sqlite_store::SqliteStore, NoteFilter, StoreAuthenticator, TransactionFilter}, sync::SyncSummary, - transactions::{DataStoreError, TransactionExecutorError, TransactionRequest}, + transactions::{ + DataStoreError, TransactionExecutorError, TransactionRequest, TransactionRequestBuilder, + }, Client, ClientError, }; use miden_objects::{ @@ -253,13 +255,14 @@ pub async fn mint_note( // Create a Mint Tx for 1000 units of our fungible asset let fungible_asset = FungibleAsset::new(faucet_account_id, MINT_AMOUNT).unwrap(); println!("Minting Asset"); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( fungible_asset, basic_account_id, note_type, client.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(client, fungible_asset.faucet_id(), tx_request.clone()).await; // Check that note is committed and return it @@ -278,7 +281,8 @@ pub async fn consume_notes( ) { println!("Consuming Note..."); let tx_request = - TransactionRequest::consume_notes(input_notes.iter().map(|n| n.id()).collect()); + TransactionRequestBuilder::consume_notes(input_notes.iter().map(|n| n.id()).collect()) + .build(); execute_tx_and_sync(client, account_id, tx_request).await; } @@ -310,7 +314,7 @@ pub async fn assert_note_cannot_be_consumed_twice( println!("Consuming Note..."); // Double-spend error expected to be received since we are consuming the same note - let tx_request = TransactionRequest::consume_notes(vec![note_to_consume_id]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![note_to_consume_id]).build(); match client.new_transaction(consuming_account_id, tx_request).await { Err(ClientError::TransactionExecutorError( TransactionExecutorError::FetchTransactionInputsFailed( @@ -345,5 +349,5 @@ pub fn mint_multiple_fungible_asset( }) .collect::>(); - TransactionRequest::new().with_own_output_notes(notes).unwrap() + TransactionRequestBuilder::new().with_own_output_notes(notes).unwrap().build() } diff --git a/tests/integration/custom_transactions_tests.rs b/tests/integration/custom_transactions_tests.rs index 5d0ad4d34..25ef3f5ce 100644 --- a/tests/integration/custom_transactions_tests.rs +++ b/tests/integration/custom_transactions_tests.rs @@ -1,7 +1,7 @@ use miden_client::{ accounts::AccountTemplate, notes::NoteExecutionHint, - transactions::TransactionRequest, + transactions::{TransactionRequest, TransactionRequestBuilder}, utils::{Deserializable, Serializable}, ZERO, }; @@ -104,11 +104,12 @@ async fn test_transaction_request() { let tx_script = client.compile_tx_script(vec![], &failure_code).unwrap(); - let transaction_request = TransactionRequest::new() + let transaction_request = TransactionRequestBuilder::new() .with_authenticated_input_notes(note_args_map.clone()) .with_custom_script(tx_script) .unwrap() - .extend_advice_map(advice_map.clone()); + .extend_advice_map(advice_map.clone()) + .build(); // This fails becuase of {asserted_value} having the incorrect number passed in assert!(client.new_transaction(regular_account.id(), transaction_request).await.is_err()); @@ -119,11 +120,12 @@ async fn test_transaction_request() { let tx_script = client.compile_tx_script(vec![], &success_code).unwrap(); - let transaction_request = TransactionRequest::new() + let transaction_request = TransactionRequestBuilder::new() .with_authenticated_input_notes(note_args_map) .with_custom_script(tx_script) .unwrap() - .extend_advice_map(advice_map); + .extend_advice_map(advice_map) + .build(); // TEST CUSTOM SCRIPT SERIALIZATION let mut buffer = Vec::new(); @@ -220,12 +222,13 @@ async fn test_merkle_store() { // Build the transaction let tx_script = client.compile_tx_script(vec![], &code).unwrap(); - let transaction_request = TransactionRequest::new() + let transaction_request = TransactionRequestBuilder::new() .with_authenticated_input_notes(note_args_map) .with_custom_script(tx_script) .unwrap() .extend_advice_map(advice_map) - .extend_merkle_store(merkle_store.inner_nodes()); + .extend_merkle_store(merkle_store.inner_nodes()) + .build(); execute_tx_and_sync(&mut client, regular_account.id(), transaction_request).await; @@ -241,9 +244,10 @@ async fn mint_custom_note( let mut random_coin = RpoRandomCoin::new(Default::default()); let note = create_custom_note(client, faucet_account_id, target_account_id, &mut random_coin); - let transaction_request = TransactionRequest::new() + let transaction_request = TransactionRequestBuilder::new() .with_own_output_notes(vec![OutputNote::Full(note.clone())]) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(client, faucet_account_id, transaction_request).await; note diff --git a/tests/integration/fpi_tests.rs b/tests/integration/fpi_tests.rs index 867d9fc30..1c6cad15a 100644 --- a/tests/integration/fpi_tests.rs +++ b/tests/integration/fpi_tests.rs @@ -1,7 +1,7 @@ use miden_client::{ accounts::{Account, AccountData, AccountTemplate, StorageSlot}, testing::prepare_word, - transactions::{TransactionKernel, TransactionRequest}, + transactions::{TransactionKernel, TransactionRequestBuilder}, Felt, Word, }; use miden_lib::accounts::auth::RpoFalcon512; @@ -52,7 +52,10 @@ async fn test_standard_fpi() { let tx = client .new_transaction( foreign_account_id, - TransactionRequest::new().with_custom_script(deployment_tx_script).unwrap(), + TransactionRequestBuilder::new() + .with_custom_script(deployment_tx_script) + .unwrap() + .build(), ) .await .unwrap(); @@ -109,11 +112,12 @@ async fn test_standard_fpi() { let tx_result = client .new_transaction( native_account.id(), - TransactionRequest::new() + TransactionRequestBuilder::new() .with_public_foreign_accounts([foreign_account_id]) .unwrap() .with_custom_script(tx_script) - .unwrap(), + .unwrap() + .build(), ) .await .unwrap(); diff --git a/tests/integration/main.rs b/tests/integration/main.rs index 4986571e3..692b416ad 100644 --- a/tests/integration/main.rs +++ b/tests/integration/main.rs @@ -12,7 +12,7 @@ use miden_client::{ sync::NoteTagSource, transactions::{ PaymentTransactionData, TransactionExecutorError, TransactionProver, - TransactionProverError, TransactionRequest, TransactionStatus, + TransactionProverError, TransactionRequestBuilder, TransactionStatus, }, ClientError, }; @@ -42,13 +42,14 @@ async fn test_added_notes() { // Mint some asset for an account not tracked by the client. It should not be stored as an // input note afterwards since it is not being tracked by the client let fungible_asset = FungibleAsset::new(faucet_account_header.id(), MINT_AMOUNT).unwrap(); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( fungible_asset, AccountId::try_from(ACCOUNT_ID_REGULAR).unwrap(), NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); println!("Running Mint tx..."); execute_tx_and_sync(&mut client, faucet_account_header.id(), tx_request).await; @@ -77,20 +78,22 @@ async fn test_multiple_tx_on_same_block() { // Do a transfer from first account to second account let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); - let tx_request_1 = TransactionRequest::pay_to_id( + let tx_request_1 = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), None, NoteType::Private, client.rng(), ) - .unwrap(); - let tx_request_2 = TransactionRequest::pay_to_id( + .unwrap() + .build(); + let tx_request_2 = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), None, NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); println!("Running P2ID tx..."); @@ -167,13 +170,15 @@ async fn test_p2id_transfer() { // Do a transfer from first account to second account let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2ID tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), None, NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); + let note = tx_request.expected_output_notes().next().unwrap().clone(); let transaction_id = execute_tx(&mut client, from_account_id, tx_request).await; @@ -202,7 +207,7 @@ async fn test_p2id_transfer() { // Consume P2ID note println!("Consuming Note..."); - let tx_request = TransactionRequest::consume_notes(vec![notes[0].id()]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![notes[0].id()]).build(); execute_tx_and_sync(&mut client, to_account_id, tx_request).await; // Ensure we have nothing else to consume @@ -258,13 +263,14 @@ async fn test_p2id_transfer_failing_not_enough_balance() { // Do a transfer from first account to second account let asset = FungibleAsset::new(faucet_account_id, MINT_AMOUNT + 1).unwrap(); println!("Running P2ID tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), None, NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_failing_tx( &mut client, from_account_id, @@ -337,13 +343,14 @@ async fn test_p2idr_transfer_consumed_by_target() { let current_block_num = client.get_sync_height().await.unwrap(); let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2IDR tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), Some(current_block_num + 50), NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(&mut client, from_account_id, tx_request.clone()).await; // Check that note is committed for the second account to consume @@ -354,7 +361,7 @@ async fn test_p2idr_transfer_consumed_by_target() { // Make the `to_account_id` consume P2IDR note let note_id = tx_request.expected_output_notes().next().unwrap().id(); println!("Consuming Note..."); - let tx_request = TransactionRequest::consume_notes(vec![note_id]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![note_id]).build(); execute_tx_and_sync(&mut client, to_account_id, tx_request).await; let regular_account = client.get_account(from_account_id).await.unwrap(); @@ -413,13 +420,14 @@ async fn test_p2idr_transfer_consumed_by_sender() { let current_block_num = client.get_sync_height().await.unwrap(); let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2IDR tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), Some(current_block_num + 5), NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(&mut client, from_account_id, tx_request).await; // Check that note is committed @@ -429,7 +437,7 @@ async fn test_p2idr_transfer_consumed_by_sender() { // Check that it's still too early to consume println!("Consuming Note (too early)..."); - let tx_request = TransactionRequest::consume_notes(vec![notes[0].id()]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![notes[0].id()]).build(); let transaction_execution_result = client.new_transaction(from_account_id, tx_request).await; assert!(transaction_execution_result.is_err_and(|err| { matches!( @@ -450,7 +458,7 @@ async fn test_p2idr_transfer_consumed_by_sender() { // Consume the note with the sender account println!("Consuming Note..."); - let tx_request = TransactionRequest::consume_notes(vec![notes[0].id()]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![notes[0].id()]).build(); execute_tx_and_sync(&mut client, from_account_id, tx_request).await; let regular_account = client.get_account(from_account_id).await.unwrap(); @@ -503,13 +511,14 @@ async fn test_get_consumable_notes() { // Do a transfer from first account to second account let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2IDR tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), Some(100), NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(&mut client, from_account_id, tx_request).await; // Check that note is consumable by both accounts @@ -566,7 +575,7 @@ async fn test_get_output_notes() { // Do a transfer from first account to second account let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2ID tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new( vec![Asset::Fungible(asset)], from_account_id, @@ -576,7 +585,8 @@ async fn test_get_output_notes() { NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); let output_note_id = tx_request.expected_output_notes().next().unwrap().id(); @@ -607,13 +617,14 @@ async fn test_import_expected_notes() { wait_for_node(&mut client_2).await; - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet_account.id(), MINT_AMOUNT).unwrap(), client_2_account.id(), NoteType::Public, client_2.rng(), ) - .unwrap(); + .unwrap() + .build(); let note: InputNoteRecord = tx_request.expected_output_notes().next().unwrap().clone().into(); client_2.sync_state().await.unwrap(); @@ -640,13 +651,14 @@ async fn test_import_expected_notes() { // If client 2 succesfully consumes the note, we confirm we have MMR and block header data consume_notes(&mut client_2, client_2_account.id(), &[input_note.try_into().unwrap()]).await; - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet_account.id(), MINT_AMOUNT).unwrap(), first_basic_account.id(), NoteType::Private, client_2.rng(), ) - .unwrap(); + .unwrap() + .build(); let note: InputNoteRecord = tx_request.expected_output_notes().next().unwrap().clone().into(); // Import an uncommited note without verification @@ -693,13 +705,14 @@ async fn test_import_expected_note_uncommitted() { wait_for_node(&mut client_2).await; - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet_account.id(), MINT_AMOUNT).unwrap(), client_2_account.id(), NoteType::Public, client_1.rng(), ) - .unwrap(); + .unwrap() + .build(); let note: InputNoteRecord = tx_request.expected_output_notes().next().unwrap().clone().into(); client_2.sync_state().await.unwrap(); @@ -736,13 +749,14 @@ async fn test_import_expected_notes_from_the_past_as_committed() { wait_for_node(&mut client_2).await; - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet_account.id(), MINT_AMOUNT).unwrap(), first_basic_account.id(), NoteType::Public, client_1.rng(), ) - .unwrap(); + .unwrap() + .build(); let note: InputNoteRecord = tx_request.expected_output_notes().next().unwrap().clone().into(); let block_height_before = client_1.get_sync_height().await.unwrap(); @@ -846,13 +860,14 @@ async fn test_sync_detail_values() { // Do a transfer with recall from first account to second account let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), Some(new_details.block_num + 5), NoteType::Public, client1.rng(), ) - .unwrap(); + .unwrap() + .build(); let note_id = tx_request.expected_output_notes().next().unwrap().id(); execute_tx_and_sync(&mut client1, from_account_id, tx_request).await; @@ -864,7 +879,7 @@ async fn test_sync_detail_values() { assert_eq!(new_details.updated_accounts.len(), 0); // Consume the note with the second account - let tx_request = TransactionRequest::consume_notes(vec![note_id]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![note_id]).build(); execute_tx_and_sync(&mut client2, to_account_id, tx_request).await; // First client sync should have a new nullifier as the note was consumed @@ -892,13 +907,14 @@ async fn test_multiple_transactions_can_be_committed_in_different_blocks_without let fungible_asset = FungibleAsset::new(faucet_account_id, MINT_AMOUNT).unwrap(); println!("Minting Asset"); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( fungible_asset, from_account_id, NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); println!("Executing transaction..."); let transaction_execution_result = @@ -918,13 +934,14 @@ async fn test_multiple_transactions_can_be_committed_in_different_blocks_without let fungible_asset = FungibleAsset::new(faucet_account_id, MINT_AMOUNT).unwrap(); println!("Minting Asset"); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( fungible_asset, from_account_id, NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); println!("Executing transaction..."); let transaction_execution_result = @@ -954,13 +971,14 @@ async fn test_multiple_transactions_can_be_committed_in_different_blocks_without let fungible_asset = FungibleAsset::new(faucet_account_id, MINT_AMOUNT).unwrap(); println!("Minting Asset"); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( fungible_asset, from_account_id, NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); println!("Executing transaction..."); let transaction_execution_result = @@ -1068,17 +1086,17 @@ async fn test_consume_multiple_expected_notes() { expected_notes.partition(|note| client_notes_ids.contains(¬e.id())); // Create and execute transactions - let tx_request_1 = TransactionRequest::consume_notes( + let tx_request_1 = TransactionRequestBuilder::consume_notes( client_owned_notes.iter().map(|note| note.id()).collect(), ) - .with_authenticated_input_notes(client_owned_notes.iter().map(|note| (note.id(), None))); + .with_authenticated_input_notes(client_owned_notes.iter().map(|note| (note.id(), None))) + .build(); - let tx_request_2 = TransactionRequest::consume_notes( + let tx_request_2 = TransactionRequestBuilder::consume_notes( unauth_owned_notes.iter().map(|note| note.id()).collect(), ) - .with_unauthenticated_input_notes( - unauth_owned_notes.iter().map(|note| ((*note).clone(), None)), - ); + .with_unauthenticated_input_notes(unauth_owned_notes.iter().map(|note| ((*note).clone(), None))) + .build(); let tx_id_1 = execute_tx(&mut client, to_account_ids[0], tx_request_1).await; let tx_id_2 = execute_tx(&mut unauth_client, to_account_ids[1], tx_request_2).await; @@ -1147,13 +1165,14 @@ async fn test_import_consumed_note_with_proof() { let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2IDR tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), Some(current_block_num), NoteType::Private, client_1.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(&mut client_1, from_account_id, tx_request).await; let note = client_1 .get_input_notes(NoteFilter::Committed) @@ -1166,7 +1185,7 @@ async fn test_import_consumed_note_with_proof() { // Consume the note with the sender account println!("Consuming Note..."); - let tx_request = TransactionRequest::consume_notes(vec![note.id()]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![note.id()]).build(); execute_tx_and_sync(&mut client_1, from_account_id, tx_request).await; // Import the consumed note @@ -1205,13 +1224,14 @@ async fn test_import_consumed_note_with_id() { let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2IDR tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), Some(current_block_num), NoteType::Public, client_1.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(&mut client_1, from_account_id, tx_request).await; let note = client_1 .get_input_notes(NoteFilter::Committed) @@ -1224,7 +1244,7 @@ async fn test_import_consumed_note_with_id() { // Consume the note with the sender account println!("Consuming Note..."); - let tx_request = TransactionRequest::consume_notes(vec![note.id()]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![note.id()]).build(); execute_tx_and_sync(&mut client_1, from_account_id, tx_request).await; client_2.sync_state().await.unwrap(); @@ -1265,13 +1285,14 @@ async fn test_discarded_transaction() { let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2IDR tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), Some(current_block_num), NoteType::Public, client_1.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(&mut client_1, from_account_id, tx_request).await; client_2.sync_state().await.unwrap(); @@ -1284,7 +1305,7 @@ async fn test_discarded_transaction() { .clone(); println!("Consuming Note..."); - let tx_request = TransactionRequest::consume_notes(vec![note.id()]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![note.id()]).build(); // Consume the note in client 1 but dont submit it to the node let tx_result = client_1.new_transaction(from_account_id, tx_request.clone()).await.unwrap(); @@ -1347,13 +1368,14 @@ async fn test_custom_transaction_prover() { let fungible_asset = FungibleAsset::new(faucet_account_id, MINT_AMOUNT).unwrap(); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( fungible_asset, from_account_id, NoteType::Private, client.rng(), ) - .unwrap(); + .unwrap() + .build(); let transaction_execution_result = client.new_transaction(faucet_account_id, tx_request.clone()).await.unwrap(); diff --git a/tests/integration/onchain_tests.rs b/tests/integration/onchain_tests.rs index e82fff5bf..d3f49916b 100644 --- a/tests/integration/onchain_tests.rs +++ b/tests/integration/onchain_tests.rs @@ -1,7 +1,7 @@ use miden_client::{ accounts::AccountTemplate, store::{InputNoteState, NoteFilter}, - transactions::{PaymentTransactionData, TransactionRequest}, + transactions::{PaymentTransactionData, TransactionRequestBuilder}, }; use miden_objects::{ accounts::{AccountId, AccountStorageMode}, @@ -53,13 +53,14 @@ async fn test_onchain_notes_flow() { client_1.sync_state().await.unwrap(); client_2.sync_state().await.unwrap(); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet_account.id(), MINT_AMOUNT).unwrap(), basic_wallet_1.id(), NoteType::Public, client_1.rng(), ) - .unwrap(); + .unwrap() + .build(); let note = tx_request.expected_output_notes().next().unwrap().clone(); execute_tx_and_sync(&mut client_1, faucet_account.id(), tx_request).await; @@ -83,7 +84,7 @@ async fn test_onchain_notes_flow() { .await; let p2id_asset = FungibleAsset::new(faucet_account.id(), TRANSFER_AMOUNT).unwrap(); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new( vec![p2id_asset.into()], basic_wallet_1.id(), @@ -93,7 +94,8 @@ async fn test_onchain_notes_flow() { NoteType::Public, client_2.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(&mut client_2, basic_wallet_1.id(), tx_request).await; // sync client 3 (basic account 2) @@ -221,13 +223,14 @@ async fn test_onchain_accounts() { let asset = FungibleAsset::new(faucet_account_id, TRANSFER_AMOUNT).unwrap(); println!("Running P2ID tx..."); - let tx_request = TransactionRequest::pay_to_id( + let tx_request = TransactionRequestBuilder::pay_to_id( PaymentTransactionData::new(vec![Asset::Fungible(asset)], from_account_id, to_account_id), None, NoteType::Public, client_1.rng(), ) - .unwrap(); + .unwrap() + .build(); execute_tx_and_sync(&mut client_1, from_account_id, tx_request).await; // sync on second client until we receive the note @@ -240,7 +243,7 @@ async fn test_onchain_accounts() { // Consume the note println!("Consuming note on second client..."); - let tx_request = TransactionRequest::consume_notes(vec![notes[0].id()]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![notes[0].id()]).build(); execute_tx_and_sync(&mut client_2, to_account_id, tx_request).await; // sync on first client @@ -301,13 +304,14 @@ async fn test_onchain_notes_sync_with_tag() { let target_account_id = AccountId::try_from(ACCOUNT_ID_REGULAR).unwrap(); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( FungibleAsset::new(faucet_account.id(), MINT_AMOUNT).unwrap(), target_account_id, NoteType::Public, client_1.rng(), ) - .unwrap(); + .unwrap() + .build(); let note = tx_request.expected_output_notes().next().unwrap().clone(); execute_tx_and_sync(&mut client_1, faucet_account.id(), tx_request).await; diff --git a/tests/integration/swap_transactions_tests.rs b/tests/integration/swap_transactions_tests.rs index 5c11ae91a..6e9c6fccf 100644 --- a/tests/integration/swap_transactions_tests.rs +++ b/tests/integration/swap_transactions_tests.rs @@ -1,7 +1,7 @@ use miden_client::{ accounts::{Account, AccountTemplate}, notes::Note, - transactions::{SwapTransactionData, TransactionRequest}, + transactions::{SwapTransactionData, TransactionRequestBuilder}, }; use miden_objects::{ accounts::{AccountId, AccountStorageMode}, @@ -99,7 +99,7 @@ async fn test_swap_fully_onchain() { println!("Consuming mint note on first client..."); - let tx_request = TransactionRequest::consume_notes(vec![account_a_mint_note_id]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![account_a_mint_note_id]).build(); execute_tx_and_sync(&mut client1, account_a.id(), tx_request).await; // Sync and consume note for accountB @@ -112,7 +112,7 @@ async fn test_swap_fully_onchain() { println!("Consuming mint note on second client..."); - let tx_request = TransactionRequest::consume_notes(vec![account_b_mint_note_id]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![account_b_mint_note_id]).build(); execute_tx_and_sync(&mut client2, account_b.id(), tx_request).await; // Create ONCHAIN swap note (clientA offers 1 BTC in exchange of 25 ETH) @@ -123,7 +123,7 @@ async fn test_swap_fully_onchain() { FungibleAsset::new(eth_faucet_account.id(), REQUESTED_ASSET_AMOUNT).unwrap(); println!("Running SWAP tx..."); - let tx_request = TransactionRequest::swap( + let tx_request = TransactionRequestBuilder::swap( SwapTransactionData::new( account_a.id(), Asset::Fungible(offered_asset), @@ -132,7 +132,8 @@ async fn test_swap_fully_onchain() { NoteType::Public, client1.rng(), ) - .unwrap(); + .unwrap() + .build(); let expected_output_notes: Vec = tx_request.expected_output_notes().cloned().collect(); let expected_payback_note_details: Vec = @@ -157,7 +158,8 @@ async fn test_swap_fully_onchain() { client2.sync_state().await.unwrap(); println!("Consuming swap note on second client..."); - let tx_request = TransactionRequest::consume_notes(vec![expected_output_notes[0].id()]); + let tx_request = + TransactionRequestBuilder::consume_notes(vec![expected_output_notes[0].id()]).build(); execute_tx_and_sync(&mut client2, account_b.id(), tx_request).await; // sync on client 1, we should get the missing payback note details. @@ -165,7 +167,9 @@ async fn test_swap_fully_onchain() { client1.sync_state().await.unwrap(); println!("Consuming swap payback note on first client..."); - let tx_request = TransactionRequest::consume_notes(vec![expected_payback_note_details[0].id()]); + let tx_request = + TransactionRequestBuilder::consume_notes(vec![expected_payback_note_details[0].id()]) + .build(); execute_tx_and_sync(&mut client1, account_a.id(), tx_request).await; // At the end we should end up with @@ -312,7 +316,7 @@ async fn test_swap_offchain() { println!("Consuming mint note on first client..."); - let tx_request = TransactionRequest::consume_notes(vec![account_a_mint_note_id]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![account_a_mint_note_id]).build(); execute_tx_and_sync(&mut client1, account_a.id(), tx_request).await; // Sync and consume note for accountB @@ -325,7 +329,7 @@ async fn test_swap_offchain() { println!("Consuming mint note on second client..."); - let tx_request = TransactionRequest::consume_notes(vec![account_b_mint_note_id]); + let tx_request = TransactionRequestBuilder::consume_notes(vec![account_b_mint_note_id]).build(); execute_tx_and_sync(&mut client2, account_b.id(), tx_request).await; // Create ONCHAIN swap note (clientA offers 1 BTC in exchange of 25 ETH) @@ -336,7 +340,7 @@ async fn test_swap_offchain() { FungibleAsset::new(eth_faucet_account.id(), REQUESTED_ASSET_AMOUNT).unwrap(); println!("Running SWAP tx..."); - let tx_request = TransactionRequest::swap( + let tx_request = TransactionRequestBuilder::swap( SwapTransactionData::new( account_a.id(), Asset::Fungible(offered_asset), @@ -345,7 +349,8 @@ async fn test_swap_offchain() { NoteType::Private, client1.rng(), ) - .unwrap(); + .unwrap() + .build(); let expected_output_notes: Vec = tx_request.expected_output_notes().cloned().collect(); let expected_payback_note_details = @@ -376,7 +381,8 @@ async fn test_swap_offchain() { // consume swap note with accountB, and check that the vault changed appropiately println!("Consuming swap note on second client..."); - let tx_request = TransactionRequest::consume_notes(vec![expected_output_notes[0].id()]); + let tx_request = + TransactionRequestBuilder::consume_notes(vec![expected_output_notes[0].id()]).build(); execute_tx_and_sync(&mut client2, account_b.id(), tx_request).await; // sync on client 1, we should get the missing payback note details. @@ -384,7 +390,9 @@ async fn test_swap_offchain() { client1.sync_state().await.unwrap(); println!("Consuming swap payback note on first client..."); - let tx_request = TransactionRequest::consume_notes(vec![expected_payback_note_details[0].id()]); + let tx_request = + TransactionRequestBuilder::consume_notes(vec![expected_payback_note_details[0].id()]) + .build(); execute_tx_and_sync(&mut client1, account_a.id(), tx_request).await; // At the end we should end up with @@ -498,13 +506,14 @@ async fn mint( let fungible_asset = FungibleAsset::new(faucet_account_id, mint_amount).unwrap(); println!("Minting Asset"); - let tx_request = TransactionRequest::mint_fungible_asset( + let tx_request = TransactionRequestBuilder::mint_fungible_asset( fungible_asset, basic_account_id, note_type, client.rng(), ) - .unwrap(); + .unwrap() + .build(); let id = tx_request.expected_output_notes().next().unwrap().id(); execute_tx_and_sync(client, faucet_account_id, tx_request.clone()).await;