From c811ac0a5e279b79ad9de3e010df40fea42244f9 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Mon, 31 Mar 2025 20:18:40 +0300 Subject: [PATCH 01/18] feat: initial working state --- crates/miden-lib/src/account/interface/mod.rs | 11 +- crates/miden-lib/src/note/well_known_note.rs | 81 ++++++++++- crates/miden-tx/src/executor/mod.rs | 137 +++++++++++++++++- crates/miden-tx/src/host/tx_progress.rs | 4 + .../src/testing/tx_context/builder.rs | 4 +- crates/miden-tx/src/tests/mod.rs | 45 +++++- 6 files changed, 272 insertions(+), 10 deletions(-) diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-lib/src/account/interface/mod.rs index df485ef71b..2be6e79df4 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-lib/src/account/interface/mod.rs @@ -5,7 +5,7 @@ use miden_objects::{ account::{Account, AccountCode, AccountId, AccountIdPrefix, AccountType}, assembly::mast::{MastForest, MastNode, MastNodeId}, crypto::dsa::rpo_falcon512, - note::{Note, NoteScript, PartialNote}, + note::{Note, NoteId, NoteScript, PartialNote}, transaction::TransactionScript, }; use thiserror::Error; @@ -313,6 +313,15 @@ pub enum NoteAccountCompatibility { Maybe, } +/// TODO: move and rename this enum +#[derive(Debug)] +pub enum ExecutionCheckResult { + Success, + + // (failing_note, successful_notes) + Failure((NoteId, Vec)), +} + // HELPER FUNCTIONS // ================================================================================================ diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index 93e6ea1a97..4430fd408e 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -1,6 +1,8 @@ use miden_objects::{ - Digest, + Digest, Felt, Word, + account::{Account, AccountId}, assembly::{ProcedureName, QualifiedProcedureName}, + asset::Asset, note::{Note, NoteScript}, utils::{Deserializable, sync::LazyLock}, vm::Program, @@ -8,7 +10,7 @@ use miden_objects::{ use crate::account::{ components::basic_wallet_library, - interface::{AccountComponentInterface, AccountInterface}, + interface::{AccountComponentInterface, AccountInterface, NoteAccountCompatibility}, }; // WELL KNOWN NOTE SCRIPTS @@ -145,4 +147,79 @@ impl WellKnownNote { }, } } + + pub fn check_note_inputs(&self, note: &Note, account: &Account) -> NoteAccountCompatibility { + match self { + WellKnownNote::P2ID => { + let note_inputs = note.inputs().values(); + if note_inputs.len() != 2 { + return NoteAccountCompatibility::No; + } + + Self::check_input_account_id(note_inputs, account.id()) + }, + WellKnownNote::P2IDR => { + let note_inputs = note.inputs().values(); + if note_inputs.len() != 3 { + return NoteAccountCompatibility::No; + } + + Self::check_input_account_id(note_inputs, account.id()) + }, + WellKnownNote::SWAP => { + let note_inputs = note.inputs().values(); + if note_inputs.len() != 10 { + return NoteAccountCompatibility::No; + } + + let asset_felts: [Felt; 4] = note_inputs[4..8].try_into().expect( + "Should be able to convert the second word from note inputs to an array of + four Felt elements", + ); + + // get the demanded asset from the note's inputs + let asset: Asset = Word::from(asset_felts) + .try_into() + .expect("Unable to construct demanded asset from the asset felts"); + + // Check that the account can cover the demanded asset + match asset { + Asset::NonFungible(non_fungible_asset) => { + if !account.vault().has_non_fungible_asset(non_fungible_asset).expect("Should be able to query has_non_fungible_asset for an Asset::NonFungible") { + return NoteAccountCompatibility::No; + } + }, + Asset::Fungible(fungible_asset) => { + let asset_faucet_id = fungible_asset.faucet_id(); + if account + .vault() + .get_balance(asset_faucet_id) + .expect("Should be able to query get_balance for an Asset::Fungible") < fungible_asset.amount() + { + return NoteAccountCompatibility::No; } + }, + } + + NoteAccountCompatibility::Maybe + }, + } + } + + fn check_input_account_id( + note_inputs: &[Felt], + account_id: AccountId, + ) -> NoteAccountCompatibility { + let account_id_felts: [Felt; 2] = note_inputs[0..2].try_into().expect( + "Should be able to convert the first two note inputs to an array of two Felt elements", + ); + + let inputs_account_id = AccountId::try_from([account_id_felts[1], account_id_felts[0]]) + .expect("invalid account ID felts"); + + if inputs_account_id != account_id { + return NoteAccountCompatibility::No; + } + + NoteAccountCompatibility::Maybe + } } diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index bdbb73bb0f..d8be5a9340 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -1,13 +1,20 @@ use alloc::{collections::BTreeSet, sync::Arc, vec::Vec}; -use miden_lib::transaction::TransactionKernel; +use miden_lib::{ + account::interface::{ExecutionCheckResult, NoteAccountCompatibility}, + note::well_known_note::WellKnownNote, + transaction::TransactionKernel, +}; use miden_objects::{ Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES, ZERO, account::{AccountCode, AccountId}, assembly::Library, block::BlockNumber, note::NoteId, - transaction::{ExecutedTransaction, TransactionArgs, TransactionInputs, TransactionScript}, + transaction::{ + ExecutedTransaction, InputNote, InputNotes, TransactionArgs, TransactionInputs, + TransactionScript, + }, vm::StackOutputs, }; use vm_processor::{AdviceInputs, ExecutionOptions, Process, RecAdviceProvider}; @@ -235,6 +242,132 @@ impl TransactionExecutor { Ok(*stack_outputs) } + + // CHECK CONSUMABILITY + // ================================================================================================ + + // TODO: update the name, add docs + #[maybe_async] + pub fn check( + &self, + account_id: AccountId, + block_ref: BlockNumber, + notes: &InputNotes, + tx_args: TransactionArgs, + ) -> Result<(), TransactionExecutorError> { + // 1. Check note inputs + + let note_ids = notes.iter().map(|input_note| input_note.id()).collect::>(); + + let tx_inputs = + maybe_await!(self.data_store.get_transaction_inputs(account_id, block_ref, ¬e_ids)) + .map_err(TransactionExecutorError::FetchTransactionInputsFailed)?; + + let input_notes = tx_inputs.input_notes().clone().into_vec(); + + for note in input_notes.iter() { + if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { + let inputs_check_result = + well_known_note.check_note_inputs(note.note(), tx_inputs.account()); + if let NoteAccountCompatibility::No = inputs_check_result { + std::println!("Note {} has incorrect inputs", note.id()); + return Ok(()) + } + } + } + + // # 2. Perform validation: outgoing notes required + + // 3. Transaction execution + let result = maybe_await!(self.iterative_executor(tx_inputs, tx_args))?; + + // how should we return the result of the check? + match result { + ExecutionCheckResult::Success => { + std::println!("All notes processed successfully"); + }, + ExecutionCheckResult::Failure((failing_note, successful_notes)) => { + std::println!("Notes execution error"); + std::println!( + "Notes processed: {} out of {}\n", + successful_notes.len() + 1, + notes.num_notes() + ); + std::println!("Failing note: {}\n", failing_note.to_hex()); + + if !successful_notes.is_empty() { + std::println!( + "Successful notes:\n{}", + successful_notes + .iter() + .map(|note| note.to_hex()) + .collect::>() + .join("\n") + ); + } + }, + } + + Ok(()) + } + + #[maybe_async] + fn iterative_executor( + &self, + tx_inputs: TransactionInputs, + tx_args: TransactionArgs, + ) -> Result { + let (stack_inputs, advice_inputs) = + TransactionKernel::prepare_inputs(&tx_inputs, &tx_args, None); + let advice_recorder: RecAdviceProvider = advice_inputs.into(); + + // load note script MAST into the MAST store + self.mast_store.load_transaction_code(&tx_inputs, &tx_args); + + let mut host = TransactionHost::new( + tx_inputs.account().into(), + advice_recorder, + self.mast_store.clone(), + self.authenticator.clone(), + self.account_codes.iter().map(|code| code.commitment()).collect(), + ) + .map_err(TransactionExecutorError::TransactionHostCreationFailed)?; + + // execute the transaction kernel + let result = vm_processor::execute( + &TransactionKernel::main(), + stack_inputs, + &mut host, + self.exec_options, + ) + .map_err(TransactionExecutorError::TransactionProgramExecutionFailed); + + match result { + Ok(_) => Ok(ExecutionCheckResult::Success), + Err(e) => { + let notes = host.tx_progress().note_execution(); + + // empty notes vector means that we didn't process the notes, so an error + // occurred somewhere else + if notes.is_empty() { + return Err(e); + } + + let (last, success_notes) = notes.split_last().expect("notes vector is empty"); + + // if the interval end of the last note is specified, then an error occurred after + // notes processing + if last.1.end().is_some() { + return Err(e); + } + + Ok(ExecutionCheckResult::Failure(( + last.0, + success_notes.iter().map(|note| note.0).collect(), + ))) + }, + } + } } // HELPER FUNCTIONS diff --git a/crates/miden-tx/src/host/tx_progress.rs b/crates/miden-tx/src/host/tx_progress.rs index 3ef9c170e9..1d818b36ef 100644 --- a/crates/miden-tx/src/host/tx_progress.rs +++ b/crates/miden-tx/src/host/tx_progress.rs @@ -132,6 +132,10 @@ impl CycleInterval { self.end = Some(e); } + pub fn end(&self) -> &Option { + &self.end + } + /// Calculate the length of the interval pub fn len(&self) -> usize { if let Some(start) = self.start { diff --git a/crates/miden-tx/src/testing/tx_context/builder.rs b/crates/miden-tx/src/testing/tx_context/builder.rs index fc6d1c5ec0..2b2ad190a0 100644 --- a/crates/miden-tx/src/testing/tx_context/builder.rs +++ b/crates/miden-tx/src/testing/tx_context/builder.rs @@ -235,7 +235,7 @@ impl TransactionContextBuilder { .unwrap() } - /// Adds one input note with a note script that creates another ouput note. + /// Adds one input note with a note script that creates another output note. fn input_note_with_one_output_note( &mut self, sender: AccountId, @@ -289,7 +289,7 @@ impl TransactionContextBuilder { .unwrap() } - /// Adds one input note with a note script that creates 2 ouput notes. + /// Adds one input note with a note script that creates 2 output notes. fn input_note_with_two_output_notes( &mut self, sender: AccountId, diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index 6d6cd40bb7..3968b931c9 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -12,7 +12,7 @@ use ::assembly::{ use miden_lib::transaction::TransactionKernel; use miden_objects::{ Felt, MIN_PROOF_SECURITY_LEVEL, Word, - account::{AccountBuilder, AccountComponent, AccountStorage, StorageSlot}, + account::{AccountBuilder, AccountComponent, AccountId, AccountStorage, StorageSlot}, assembly::DefaultSourceManager, asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}, note::{ @@ -23,10 +23,10 @@ use miden_objects::{ account_component::AccountMockComponent, account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_SENDER, }, constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}, - note::DEFAULT_NOTE_CODE, + note::{DEFAULT_NOTE_CODE, NoteBuilder}, storage::{STORAGE_INDEX_0, STORAGE_INDEX_2}, }, transaction::{ProvenTransaction, TransactionArgs, TransactionScript}, @@ -1047,3 +1047,42 @@ fn test_execute_program() { assert_eq!(stack_outputs[..3], [Felt::new(7), Felt::new(2), ONE]); } + +#[test] +fn test_check_note_consumability() { + let sender = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap(); + + let failing_note_1 = NoteBuilder::new( + sender, + ChaCha20Rng::from_seed(ChaCha20Rng::from_seed([0_u8; 32]).random()), + ) + .code("begin push.1 drop push.0 div end") + .build(&TransactionKernel::testing_assembler()) + .unwrap(); + + let failing_note_2 = NoteBuilder::new( + sender, + ChaCha20Rng::from_seed(ChaCha20Rng::from_seed([0_u8; 32]).random()), + ) + .code("begin push.2 drop push.0 div end") + .build(&TransactionKernel::testing_assembler()) + .unwrap(); + + let tx_context = TransactionContextBuilder::with_standard_account(ONE) + .with_mock_notes_preserved() + .input_notes(vec![failing_note_1, failing_note_2]) + .build(); + + let account_id = tx_context.account().id(); + + let block_ref = tx_context.tx_inputs().block_header().block_num(); + + let executor: TransactionExecutor = + TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); + + let input_notes = tx_context.tx_inputs().input_notes(); + + executor + .check(account_id, block_ref, input_notes, tx_context.tx_args().clone()) + .unwrap(); +} From e44f59917f3c4788dfd0cc224948a681e368c467 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Thu, 3 Apr 2025 17:00:27 +0300 Subject: [PATCH 02/18] refactor: change traces to emits --- bin/bench-tx/bench-tx.json | 22 ++--- .../asm/kernels/transaction/main.masm | 87 ++++++++----------- crates/miden-lib/src/transaction/events.rs | 87 +++++++++---------- crates/miden-lib/src/transaction/mod.rs | 2 +- crates/miden-tx/src/executor/mod.rs | 2 +- crates/miden-tx/src/host/mod.rs | 62 ++++++++----- 6 files changed, 131 insertions(+), 131 deletions(-) diff --git a/bin/bench-tx/bench-tx.json b/bin/bench-tx/bench-tx.json index ea7c983c3a..9937b335b4 100644 --- a/bin/bench-tx/bench-tx.json +++ b/bin/bench-tx/bench-tx.json @@ -1,21 +1,21 @@ { "simple": { - "prologue": 4378, - "notes_processing": 2310, + "prologue": 4380, + "notes_processing": 2307, "note_execution": { - "0x21b44377de8dd01d136a70800cb66bb08f0a2e7b70f77a8781fad1719baf145a": 1489, - "0xa7394456a1ba2808b1d27c6f91e9830ccd9d0e3e128bc5fcb632c74cca3d487b": 781 + "0x21b44377de8dd01d136a70800cb66bb08f0a2e7b70f77a8781fad1719baf145a": 1487, + "0xa7394456a1ba2808b1d27c6f91e9830ccd9d0e3e128bc5fcb632c74cca3d487b": 779 }, - "tx_script_processing": 46, - "epilogue": 2383 + "tx_script_processing": 45, + "epilogue": 2385 }, "p2id": { - "prologue": 2653, - "notes_processing": 1279, + "prologue": 2655, + "notes_processing": 1277, "note_execution": { - "0xce602ae68983c400dd1fb71154e6c752174da8809e8c107a0edff6a3a96618a4": 1246 + "0xce602ae68983c400dd1fb71154e6c752174da8809e8c107a0edff6a3a96618a4": 1244 }, - "tx_script_processing": 60872, - "epilogue": 430 + "tx_script_processing": 60871, + "epilogue": 432 } } \ No newline at end of file diff --git a/crates/miden-lib/asm/kernels/transaction/main.masm b/crates/miden-lib/asm/kernels/transaction/main.masm index dcafc98e7d..9924034756 100644 --- a/crates/miden-lib/asm/kernels/transaction/main.masm +++ b/crates/miden-lib/asm/kernels/transaction/main.masm @@ -5,33 +5,33 @@ use.kernel::memory use.kernel::note use.kernel::prologue -# TRACES +# EVENTS # ================================================================================================= -# Trace emitted to signal that an execution of the transaction prologue has started. -const.PROLOGUE_START=131072 -# Trace emitted to signal that an execution of the transaction prologue has ended. -const.PROLOGUE_END=131073 +# Event emitted to signal that an execution of the transaction prologue has started. +const.PROLOGUE_START=131088 +# Event emitted to signal that an execution of the transaction prologue has ended. +const.PROLOGUE_END=131089 -# Trace emitted to signal that the notes processing has started. -const.NOTES_PROCESSING_START=131074 -# Trace emitted to signal that the notes processing has ended. -const.NOTES_PROCESSING_END=131075 +# Event emitted to signal that the notes processing has started. +const.NOTES_PROCESSING_START=131090 +# Event emitted to signal that the notes processing has ended. +const.NOTES_PROCESSING_END=131091 -# Trace emitted to signal that the note consuming has started. -const.NOTE_EXECUTION_START=131076 -# Trace emitted to signal that the note consuming has ended. -const.NOTE_EXECUTION_END=131077 +# Event emitted to signal that the note consuming has started. +const.NOTE_EXECUTION_START=131092 +# Event emitted to signal that the note consuming has ended. +const.NOTE_EXECUTION_END=131093 -# Trace emitted to signal that the transaction script processing has started. -const.TX_SCRIPT_PROCESSING_START=131078 -# Trace emitted to signal that the transaction script processing has ended. -const.TX_SCRIPT_PROCESSING_END=131079 +# Event emitted to signal that the transaction script processing has started. +const.TX_SCRIPT_PROCESSING_START=131094 +# Event emitted to signal that the transaction script processing has ended. +const.TX_SCRIPT_PROCESSING_END=131095 -# Trace emitted to signal that an execution of the transaction epilogue has started. -const.EPILOGUE_START=131080 -# Trace emitted to signal that an execution of the transaction epilogue has ended. -const.EPILOGUE_END=131081 +# Event emitted to signal that an execution of the transaction epilogue has started. +const.EPILOGUE_START=131096 +# Event emitted to signal that an execution of the transaction epilogue has ended. +const.EPILOGUE_END=131097 # MAIN # ================================================================================================= @@ -67,23 +67,18 @@ const.EPILOGUE_END=131081 proc.main.1 # Prologue # --------------------------------------------------------------------------------------------- - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.91683607 drop - trace.PROLOGUE_START + + emit.PROLOGUE_START exec.prologue::prepare_transaction # => [] - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.3551582517 drop - trace.PROLOGUE_END + emit.PROLOGUE_END # Note Processing # --------------------------------------------------------------------------------------------- - - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.1597639019 drop - trace.NOTES_PROCESSING_START + + emit.NOTES_PROCESSING_START exec.memory::get_num_input_notes # => [num_input_notes] @@ -96,9 +91,7 @@ proc.main.1 # => [should_loop] while.true - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.1161965828 drop - trace.NOTE_EXECUTION_START + emit.NOTE_EXECUTION_START # => [] exec.note::prepare_note @@ -119,24 +112,18 @@ proc.main.1 loc_load.0 neq # => [should_loop] - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.1680624729 drop - trace.NOTE_EXECUTION_END + emit.NOTE_EXECUTION_END end exec.note::note_processing_teardown # => [] - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.920581323 drop - trace.NOTES_PROCESSING_END + emit.NOTES_PROCESSING_END # Transaction Script Processing # --------------------------------------------------------------------------------------------- - - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.4103684209 drop - trace.TX_SCRIPT_PROCESSING_START + + emit.TX_SCRIPT_PROCESSING_START # get the memory address of the transaction script root and load it to the stack exec.memory::get_tx_script_root_ptr @@ -160,16 +147,12 @@ proc.main.1 # => [] end - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.1155406220 drop - trace.TX_SCRIPT_PROCESSING_END + emit.TX_SCRIPT_PROCESSING_END # Epilogue # --------------------------------------------------------------------------------------------- - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.1887961092 drop - trace.EPILOGUE_START + emit.EPILOGUE_START # execute the transaction epilogue exec.epilogue::finalize_transaction @@ -178,9 +161,7 @@ proc.main.1 # truncate the stack movupw.3 dropw movupw.3 dropw movup.9 drop - # use `push.* drop` instructions before `trace` to make sure that MAST root will be unique - push.3456069754 drop - trace.EPILOGUE_END + emit.EPILOGUE_END # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_COMMITMENT, tx_expiration_block_num] end diff --git a/crates/miden-lib/src/transaction/events.rs b/crates/miden-lib/src/transaction/events.rs index f3eccfa0bd..165459ec3d 100644 --- a/crates/miden-lib/src/transaction/events.rs +++ b/crates/miden-lib/src/transaction/events.rs @@ -1,6 +1,6 @@ use core::fmt; -use super::{TransactionEventError, TransactionTraceParsingError}; +use super::TransactionEventError; // CONSTANTS // ================================================================================================ @@ -33,6 +33,21 @@ const NOTE_AFTER_ADD_ASSET: u32 = 0x2_000e; // 131086 const FALCON_SIG_TO_STACK: u32 = 0x2_000f; // 131087 +const PROLOGUE_START: u32 = 0x2_0010; // 131088 +const PROLOGUE_END: u32 = 0x2_0011; // 131089 + +const NOTES_PROCESSING_START: u32 = 0x2_0012; // 131090 +const NOTES_PROCESSING_END: u32 = 0x2_0013; // 131091 + +const NOTE_EXECUTION_START: u32 = 0x2_0014; // 131092 +const NOTE_EXECUTION_END: u32 = 0x2_0015; // 131093 + +const TX_SCRIPT_PROCESSING_START: u32 = 0x2_0016; // 131094 +const TX_SCRIPT_PROCESSING_END: u32 = 0x2_0017; // 131095 + +const EPILOGUE_START: u32 = 0x2_0018; // 131096 +const EPILOGUE_END: u32 = 0x2_0019; // 131097 + /// Events which may be emitted by a transaction kernel. /// /// The events are emitted via the `emit.` instruction. The event ID is a 32-bit @@ -67,6 +82,21 @@ pub enum TransactionEvent { NoteAfterAddAsset = NOTE_AFTER_ADD_ASSET, FalconSigToStack = FALCON_SIG_TO_STACK, + + PrologueStart = PROLOGUE_START, + PrologueEnd = PROLOGUE_END, + + NotesProcessingStart = NOTES_PROCESSING_START, + NotesProcessingEnd = NOTES_PROCESSING_END, + + NoteExecutionStart = NOTE_EXECUTION_START, + NoteExecutionEnd = NOTE_EXECUTION_END, + + TxScriptProcessingStart = TX_SCRIPT_PROCESSING_START, + TxScriptProcessingEnd = TX_SCRIPT_PROCESSING_END, + + EpilogueStart = EPILOGUE_START, + EpilogueEnd = EPILOGUE_END, } impl TransactionEvent { @@ -120,55 +150,22 @@ impl TryFrom for TransactionEvent { FALCON_SIG_TO_STACK => Ok(TransactionEvent::FalconSigToStack), - _ => Err(TransactionEventError::InvalidTransactionEvent(value)), - } - } -} - -// TRANSACTION TRACE -// ================================================================================================ + PROLOGUE_START => Ok(TransactionEvent::PrologueStart), + PROLOGUE_END => Ok(TransactionEvent::PrologueEnd), -#[repr(u32)] -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum TransactionTrace { - PrologueStart = 0x2_0000, // 131072 - PrologueEnd = 0x2_0001, // 131073 - NotesProcessingStart = 0x2_0002, // 131074 - NotesProcessingEnd = 0x2_0003, // 131075 - NoteExecutionStart = 0x2_0004, // 131076 - NoteExecutionEnd = 0x2_0005, // 131077 - TxScriptProcessingStart = 0x2_0006, // 131078 - TxScriptProcessingEnd = 0x2_0007, // 131079 - EpilogueStart = 0x2_0008, // 131080 - EpilogueEnd = 0x2_0009, // 131081 -} + NOTES_PROCESSING_START => Ok(TransactionEvent::NotesProcessingStart), + NOTES_PROCESSING_END => Ok(TransactionEvent::NotesProcessingEnd), -impl fmt::Display for TransactionTrace { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self:?}") - } -} + NOTE_EXECUTION_START => Ok(TransactionEvent::NoteExecutionStart), + NOTE_EXECUTION_END => Ok(TransactionEvent::NoteExecutionEnd), -impl TryFrom for TransactionTrace { - type Error = TransactionTraceParsingError; + TX_SCRIPT_PROCESSING_START => Ok(TransactionEvent::TxScriptProcessingStart), + TX_SCRIPT_PROCESSING_END => Ok(TransactionEvent::TxScriptProcessingEnd), - fn try_from(value: u32) -> Result { - if value >> 16 != TransactionEvent::ID_PREFIX { - return Err(TransactionTraceParsingError::UnknownTransactionTrace(value)); - } + EPILOGUE_START => Ok(TransactionEvent::EpilogueStart), + EPILOGUE_END => Ok(TransactionEvent::EpilogueEnd), - match value { - 0x2_0000 => Ok(TransactionTrace::PrologueStart), - 0x2_0001 => Ok(TransactionTrace::PrologueEnd), - 0x2_0002 => Ok(TransactionTrace::NotesProcessingStart), - 0x2_0003 => Ok(TransactionTrace::NotesProcessingEnd), - 0x2_0004 => Ok(TransactionTrace::NoteExecutionStart), - 0x2_0005 => Ok(TransactionTrace::NoteExecutionEnd), - 0x2_0006 => Ok(TransactionTrace::TxScriptProcessingStart), - 0x2_0007 => Ok(TransactionTrace::TxScriptProcessingEnd), - 0x2_0008 => Ok(TransactionTrace::EpilogueStart), - 0x2_0009 => Ok(TransactionTrace::EpilogueEnd), - _ => Err(TransactionTraceParsingError::UnknownTransactionTrace(value)), + _ => Err(TransactionEventError::InvalidTransactionEvent(value)), } } } diff --git a/crates/miden-lib/src/transaction/mod.rs b/crates/miden-lib/src/transaction/mod.rs index 81e7b4f7de..714145c6f0 100644 --- a/crates/miden-lib/src/transaction/mod.rs +++ b/crates/miden-lib/src/transaction/mod.rs @@ -20,7 +20,7 @@ use super::MidenLib; pub mod memory; mod events; -pub use events::{TransactionEvent, TransactionTrace}; +pub use events::TransactionEvent; mod inputs; diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index d8be5a9340..0b5244b5bd 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -271,7 +271,7 @@ impl TransactionExecutor { well_known_note.check_note_inputs(note.note(), tx_inputs.account()); if let NoteAccountCompatibility::No = inputs_check_result { std::println!("Note {} has incorrect inputs", note.id()); - return Ok(()) + return Ok(()); } } } diff --git a/crates/miden-tx/src/host/mod.rs b/crates/miden-tx/src/host/mod.rs index e080240220..d1449d1f14 100644 --- a/crates/miden-tx/src/host/mod.rs +++ b/crates/miden-tx/src/host/mod.rs @@ -9,7 +9,7 @@ use alloc::{ use miden_lib::{ errors::tx_kernel_errors::TX_KERNEL_ERRORS, transaction::{ - TransactionEvent, TransactionEventError, TransactionKernelError, TransactionTrace, + TransactionEvent, TransactionEventError, TransactionKernelError, memory::{CURRENT_INPUT_NOTE_PTR, NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR}, }, }; @@ -537,34 +537,56 @@ impl Host for TransactionHost { TransactionEvent::NoteAfterAddAsset => Ok(()), TransactionEvent::FalconSigToStack => self.on_signature_requested(process), - } - .map_err(|err| ExecutionError::EventError(Box::new(err)))?; - Ok(()) - } + TransactionEvent::PrologueStart => { + self.tx_progress.start_prologue(process.clk()); + Ok(()) + }, + TransactionEvent::PrologueEnd => { + self.tx_progress.end_prologue(process.clk()); + Ok(()) + }, - fn on_trace(&mut self, process: ProcessState, trace_id: u32) -> Result<(), ExecutionError> { - let event = TransactionTrace::try_from(trace_id) - .map_err(|err| ExecutionError::EventError(Box::new(err)))?; + TransactionEvent::NotesProcessingStart => { + self.tx_progress.start_notes_processing(process.clk()); + Ok(()) + }, + TransactionEvent::NotesProcessingEnd => { + self.tx_progress.end_notes_processing(process.clk()); + Ok(()) + }, - use TransactionTrace::*; - match event { - PrologueStart => self.tx_progress.start_prologue(process.clk()), - PrologueEnd => self.tx_progress.end_prologue(process.clk()), - NotesProcessingStart => self.tx_progress.start_notes_processing(process.clk()), - NotesProcessingEnd => self.tx_progress.end_notes_processing(process.clk()), - NoteExecutionStart => { + TransactionEvent::NoteExecutionStart => { let note_id = Self::get_current_note_id(process)?.expect( "Note execution interval measurement is incorrect: check the placement of the start and the end of the interval", ); self.tx_progress.start_note_execution(process.clk(), note_id); + Ok(()) }, - NoteExecutionEnd => self.tx_progress.end_note_execution(process.clk()), - TxScriptProcessingStart => self.tx_progress.start_tx_script_processing(process.clk()), - TxScriptProcessingEnd => self.tx_progress.end_tx_script_processing(process.clk()), - EpilogueStart => self.tx_progress.start_epilogue(process.clk()), - EpilogueEnd => self.tx_progress.end_epilogue(process.clk()), + TransactionEvent::NoteExecutionEnd => { + self.tx_progress.end_note_execution(process.clk()); + Ok(()) + }, + + TransactionEvent::TxScriptProcessingStart => { + self.tx_progress.start_tx_script_processing(process.clk()); + Ok(()) + } + TransactionEvent::TxScriptProcessingEnd => { + self.tx_progress.end_tx_script_processing(process.clk()); + Ok(()) + } + + TransactionEvent::EpilogueStart => { + self.tx_progress.start_epilogue(process.clk()); + Ok(()) + } + TransactionEvent::EpilogueEnd => { + self.tx_progress.end_epilogue(process.clk()); + Ok(()) + } } + .map_err(|err| ExecutionError::EventError(Box::new(err)))?; Ok(()) } From 6164c5265e02cd711b65a5fea4be0984e0f84ff4 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Fri, 4 Apr 2025 17:54:46 +0300 Subject: [PATCH 03/18] refactor: update checker returning value --- crates/miden-lib/src/account/interface/mod.rs | 11 +--- crates/miden-lib/src/note/well_known_note.rs | 31 +++++++--- crates/miden-tx/src/executor/mod.rs | 59 +++++++------------ crates/miden-tx/src/tests/mod.rs | 19 ++++-- 4 files changed, 57 insertions(+), 63 deletions(-) diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-lib/src/account/interface/mod.rs index 2be6e79df4..df485ef71b 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-lib/src/account/interface/mod.rs @@ -5,7 +5,7 @@ use miden_objects::{ account::{Account, AccountCode, AccountId, AccountIdPrefix, AccountType}, assembly::mast::{MastForest, MastNode, MastNodeId}, crypto::dsa::rpo_falcon512, - note::{Note, NoteId, NoteScript, PartialNote}, + note::{Note, NoteScript, PartialNote}, transaction::TransactionScript, }; use thiserror::Error; @@ -313,15 +313,6 @@ pub enum NoteAccountCompatibility { Maybe, } -/// TODO: move and rename this enum -#[derive(Debug)] -pub enum ExecutionCheckResult { - Success, - - // (failing_note, successful_notes) - Failure((NoteId, Vec)), -} - // HELPER FUNCTIONS // ================================================================================================ diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index 4430fd408e..64067c1182 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -148,7 +148,19 @@ impl WellKnownNote { } } - pub fn check_note_inputs(&self, note: &Note, account: &Account) -> NoteAccountCompatibility { + /// Checks the correctness of the provided note inputs against the target account. + /// + /// It performs: + /// - for all notes: a check that note inputs have correct number of values. + /// - for `P2ID` and `P2IDR` notes: assertion that the account ID provided by the note inputs is + /// equal to the target account ID. + /// - for `SWAP` note: a check that the target account has sufficient amount of assets required + /// by the note. + pub fn check_note_inputs( + &self, + note: &Note, + target_account: &Account, + ) -> NoteAccountCompatibility { match self { WellKnownNote::P2ID => { let note_inputs = note.inputs().values(); @@ -156,7 +168,7 @@ impl WellKnownNote { return NoteAccountCompatibility::No; } - Self::check_input_account_id(note_inputs, account.id()) + Self::check_input_account_id(note_inputs, target_account.id()) }, WellKnownNote::P2IDR => { let note_inputs = note.inputs().values(); @@ -164,7 +176,7 @@ impl WellKnownNote { return NoteAccountCompatibility::No; } - Self::check_input_account_id(note_inputs, account.id()) + Self::check_input_account_id(note_inputs, target_account.id()) }, WellKnownNote::SWAP => { let note_inputs = note.inputs().values(); @@ -185,18 +197,19 @@ impl WellKnownNote { // Check that the account can cover the demanded asset match asset { Asset::NonFungible(non_fungible_asset) => { - if !account.vault().has_non_fungible_asset(non_fungible_asset).expect("Should be able to query has_non_fungible_asset for an Asset::NonFungible") { + if !target_account.vault().has_non_fungible_asset(non_fungible_asset).expect("Should be able to query has_non_fungible_asset for an Asset::NonFungible") { return NoteAccountCompatibility::No; } }, Asset::Fungible(fungible_asset) => { let asset_faucet_id = fungible_asset.faucet_id(); - if account + if target_account .vault() .get_balance(asset_faucet_id) .expect("Should be able to query get_balance for an Asset::Fungible") < fungible_asset.amount() { - return NoteAccountCompatibility::No; } + return NoteAccountCompatibility::No; + } }, } @@ -205,9 +218,11 @@ impl WellKnownNote { } } + /// Checks that the account ID, created from the first two values of the note inputs, is the + /// same as the ID of the target account. fn check_input_account_id( note_inputs: &[Felt], - account_id: AccountId, + target_account_id: AccountId, ) -> NoteAccountCompatibility { let account_id_felts: [Felt; 2] = note_inputs[0..2].try_into().expect( "Should be able to convert the first two note inputs to an array of two Felt elements", @@ -216,7 +231,7 @@ impl WellKnownNote { let inputs_account_id = AccountId::try_from([account_id_felts[1], account_id_felts[0]]) .expect("invalid account ID felts"); - if inputs_account_id != account_id { + if inputs_account_id != target_account_id { return NoteAccountCompatibility::No; } diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 0b5244b5bd..3d89e21a34 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -1,8 +1,7 @@ use alloc::{collections::BTreeSet, sync::Arc, vec::Vec}; use miden_lib::{ - account::interface::{ExecutionCheckResult, NoteAccountCompatibility}, - note::well_known_note::WellKnownNote, + account::interface::NoteAccountCompatibility, note::well_known_note::WellKnownNote, transaction::TransactionKernel, }; use miden_objects::{ @@ -244,7 +243,7 @@ impl TransactionExecutor { } // CHECK CONSUMABILITY - // ================================================================================================ + // ============================================================================================ // TODO: update the name, add docs #[maybe_async] @@ -254,8 +253,9 @@ impl TransactionExecutor { block_ref: BlockNumber, notes: &InputNotes, tx_args: TransactionArgs, - ) -> Result<(), TransactionExecutorError> { - // 1. Check note inputs + ) -> Result { + // Check note inputs + // ---------------------------------------------------------------------------------------- let note_ids = notes.iter().map(|input_note| input_note.id()).collect::>(); @@ -270,45 +270,15 @@ impl TransactionExecutor { let inputs_check_result = well_known_note.check_note_inputs(note.note(), tx_inputs.account()); if let NoteAccountCompatibility::No = inputs_check_result { - std::println!("Note {} has incorrect inputs", note.id()); - return Ok(()); + return Ok(ExecutionCheckResult::Failure((note.id(), vec![]))); } } } - // # 2. Perform validation: outgoing notes required - - // 3. Transaction execution - let result = maybe_await!(self.iterative_executor(tx_inputs, tx_args))?; + // Execute transaction + // ---------------------------------------------------------------------------------------- - // how should we return the result of the check? - match result { - ExecutionCheckResult::Success => { - std::println!("All notes processed successfully"); - }, - ExecutionCheckResult::Failure((failing_note, successful_notes)) => { - std::println!("Notes execution error"); - std::println!( - "Notes processed: {} out of {}\n", - successful_notes.len() + 1, - notes.num_notes() - ); - std::println!("Failing note: {}\n", failing_note.to_hex()); - - if !successful_notes.is_empty() { - std::println!( - "Successful notes:\n{}", - successful_notes - .iter() - .map(|note| note.to_hex()) - .collect::>() - .join("\n") - ); - } - }, - } - - Ok(()) + maybe_await!(self.iterative_executor(tx_inputs, tx_args)) } #[maybe_async] @@ -430,3 +400,14 @@ fn build_executed_transaction( tx_progress.into(), )) } + +/// Describes whether a transaction with a specified set of notes could be executed against target +/// account. +/// +/// [ExecutionCheckResult::Failure] holds a tuple, the first value of which is a failing note ID, +/// and the second is a vector of note IDs which were successfully executed. +#[derive(Debug, PartialEq)] +pub enum ExecutionCheckResult { + Success, + Failure((NoteId, Vec)), +} diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index 3968b931c9..5bbab5d20a 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -44,7 +44,9 @@ use super::{ LocalTransactionProver, TransactionExecutor, TransactionHost, TransactionProver, TransactionVerifier, }; -use crate::{TransactionMastStore, testing::TransactionContextBuilder}; +use crate::{ + TransactionMastStore, executor::ExecutionCheckResult, testing::TransactionContextBuilder, +}; mod kernel_tests; @@ -1070,19 +1072,24 @@ fn test_check_note_consumability() { let tx_context = TransactionContextBuilder::with_standard_account(ONE) .with_mock_notes_preserved() - .input_notes(vec![failing_note_1, failing_note_2]) + .input_notes(vec![failing_note_1, failing_note_2.clone()]) .build(); + let input_notes = tx_context.input_notes(); + let input_note_ids = + input_notes.iter().map(|input_note| input_note.id()).collect::>(); let account_id = tx_context.account().id(); - let block_ref = tx_context.tx_inputs().block_header().block_num(); let executor: TransactionExecutor = TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); - let input_notes = tx_context.tx_inputs().input_notes(); - - executor + let execution_check_result: ExecutionCheckResult = executor .check(account_id, block_ref, input_notes, tx_context.tx_args().clone()) .unwrap(); + + assert_eq!( + execution_check_result, + ExecutionCheckResult::Failure((failing_note_2.id(), vec![input_note_ids[0]])) + ); } From 59d2922486952e1d6ce9c0634adf89f54ca40cfa Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Fri, 4 Apr 2025 20:44:06 +0300 Subject: [PATCH 04/18] test: slightly improve the test --- crates/miden-tx/src/executor/mod.rs | 2 +- crates/miden-tx/src/tests/mod.rs | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 3d89e21a34..ca2f8a6ac0 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -247,7 +247,7 @@ impl TransactionExecutor { // TODO: update the name, add docs #[maybe_async] - pub fn check( + pub fn check_notes_consumability( &self, account_id: AccountId, block_ref: BlockNumber, diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index 5bbab5d20a..1e2cbc7243 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -1052,6 +1052,30 @@ fn test_execute_program() { #[test] fn test_check_note_consumability() { + // Success + // -------------------------------------------------------------------------------------------- + let tx_context = TransactionContextBuilder::with_standard_account(ONE) + .with_mock_notes_preserved() + .build(); + + let input_notes = tx_context.input_notes(); + let account_id = tx_context.account().id(); + let block_ref = tx_context.tx_inputs().block_header().block_num(); + + let executor: TransactionExecutor = + TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); + + let execution_check_result: ExecutionCheckResult = executor + .check_notes_consumability(account_id, block_ref, input_notes, tx_context.tx_args().clone()) + .unwrap(); + + assert_eq!( + execution_check_result, + ExecutionCheckResult::Success, + ); + + // Failure + // -------------------------------------------------------------------------------------------- let sender = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap(); let failing_note_1 = NoteBuilder::new( @@ -1085,7 +1109,7 @@ fn test_check_note_consumability() { TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); let execution_check_result: ExecutionCheckResult = executor - .check(account_id, block_ref, input_notes, tx_context.tx_args().clone()) + .check_notes_consumability(account_id, block_ref, input_notes, tx_context.tx_args().clone()) .unwrap(); assert_eq!( From 2d892c23040403be5cee3f7f2beb63f08dc0f45e Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Fri, 4 Apr 2025 21:15:20 +0300 Subject: [PATCH 05/18] chore: update changelog and comments --- CHANGELOG.md | 1 + crates/miden-tx/src/executor/mod.rs | 24 +++++++++++++++++++++--- crates/miden-tx/src/tests/mod.rs | 5 +---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3213ebec94..1552ffc852 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Implement user-facing bech32 encoding for `AccountId`s (#1185). - Implemented `execute_tx_view_script` procedure for the `TransactionExecutor` (#1197). - Enabled nested FPI calls (#1227). +- Implement `check_notes_consumability` procedure for the `TransactionExecutor` (#1269). ### Changes diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index ca2f8a6ac0..9510aea0de 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -245,7 +245,16 @@ impl TransactionExecutor { // CHECK CONSUMABILITY // ============================================================================================ - // TODO: update the name, add docs + /// Checks whether the provided input notes could be consumed by the provided account. + /// + /// This check consists of two main steps: + /// - Check whether there are "well known" notes (`P2ID`, `P2IDR` and `SWAP`) in the list of the + /// provided input notes. If so, assert that the note inputs are correct. + /// - Execute the transaction with specified notes. + /// - Returns [`ExecutionCheckResult::Success`] if the execution was successful. + /// - Returns [`ExecutionCheckResult::Failure`] if some note returned an error. The tuple + /// associated with `Failure` variant contains the ID of the failing note and a vector of + /// note IDs, which were successfully executed. #[maybe_async] pub fn check_notes_consumability( &self, @@ -278,11 +287,20 @@ impl TransactionExecutor { // Execute transaction // ---------------------------------------------------------------------------------------- - maybe_await!(self.iterative_executor(tx_inputs, tx_args)) + maybe_await!(self.notes_execution_progress_checker(tx_inputs, tx_args)) } + /// Executes the transaction with specified notes, returning the [ExecutionCheckResult::Success] + /// if all notes has been consumed successfully and [ExecutionCheckResult::Failure] if some note + /// returned an error. + /// + /// # Errors: + /// Returns an error if: + /// - If required data can not be fetched from the [DataStore]. + /// - If the transaction host can not be created from the provided values. + /// - If the execution of the provided program fails on the stage other than note execution. #[maybe_async] - fn iterative_executor( + fn notes_execution_progress_checker( &self, tx_inputs: TransactionInputs, tx_args: TransactionArgs, diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index 1e2cbc7243..63dab08681 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -1069,10 +1069,7 @@ fn test_check_note_consumability() { .check_notes_consumability(account_id, block_ref, input_notes, tx_context.tx_args().clone()) .unwrap(); - assert_eq!( - execution_check_result, - ExecutionCheckResult::Success, - ); + assert_eq!(execution_check_result, ExecutionCheckResult::Success,); // Failure // -------------------------------------------------------------------------------------------- From c2c0fac53c66a51d939482ba891d27519a619cef Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Mon, 7 Apr 2025 23:56:07 +0300 Subject: [PATCH 06/18] refactor: lift requirement for Account in check_note_inputs --- crates/miden-lib/src/note/well_known_note.rs | 43 +++----------------- crates/miden-tx/src/executor/mod.rs | 26 ++++++------ 2 files changed, 19 insertions(+), 50 deletions(-) diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index 64067c1182..cf34546c6b 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -1,8 +1,7 @@ use miden_objects::{ - Digest, Felt, Word, - account::{Account, AccountId}, + Digest, Felt, + account::AccountId, assembly::{ProcedureName, QualifiedProcedureName}, - asset::Asset, note::{Note, NoteScript}, utils::{Deserializable, sync::LazyLock}, vm::Program, @@ -159,7 +158,7 @@ impl WellKnownNote { pub fn check_note_inputs( &self, note: &Note, - target_account: &Account, + target_account_id: AccountId, ) -> NoteAccountCompatibility { match self { WellKnownNote::P2ID => { @@ -168,7 +167,7 @@ impl WellKnownNote { return NoteAccountCompatibility::No; } - Self::check_input_account_id(note_inputs, target_account.id()) + Self::check_input_account_id(note_inputs, target_account_id) }, WellKnownNote::P2IDR => { let note_inputs = note.inputs().values(); @@ -176,43 +175,13 @@ impl WellKnownNote { return NoteAccountCompatibility::No; } - Self::check_input_account_id(note_inputs, target_account.id()) + Self::check_input_account_id(note_inputs, target_account_id) }, WellKnownNote::SWAP => { - let note_inputs = note.inputs().values(); - if note_inputs.len() != 10 { + if note.inputs().values().len() != 10 { return NoteAccountCompatibility::No; } - let asset_felts: [Felt; 4] = note_inputs[4..8].try_into().expect( - "Should be able to convert the second word from note inputs to an array of - four Felt elements", - ); - - // get the demanded asset from the note's inputs - let asset: Asset = Word::from(asset_felts) - .try_into() - .expect("Unable to construct demanded asset from the asset felts"); - - // Check that the account can cover the demanded asset - match asset { - Asset::NonFungible(non_fungible_asset) => { - if !target_account.vault().has_non_fungible_asset(non_fungible_asset).expect("Should be able to query has_non_fungible_asset for an Asset::NonFungible") { - return NoteAccountCompatibility::No; - } - }, - Asset::Fungible(fungible_asset) => { - let asset_faucet_id = fungible_asset.faucet_id(); - if target_account - .vault() - .get_balance(asset_faucet_id) - .expect("Should be able to query get_balance for an Asset::Fungible") < fungible_asset.amount() - { - return NoteAccountCompatibility::No; - } - }, - } - NoteAccountCompatibility::Maybe }, } diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 9510aea0de..ddfedca652 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -254,7 +254,7 @@ impl TransactionExecutor { /// - Returns [`ExecutionCheckResult::Success`] if the execution was successful. /// - Returns [`ExecutionCheckResult::Failure`] if some note returned an error. The tuple /// associated with `Failure` variant contains the ID of the failing note and a vector of - /// note IDs, which were successfully executed. + /// IDs of the notes, which were successfully executed. #[maybe_async] pub fn check_notes_consumability( &self, @@ -266,18 +266,10 @@ impl TransactionExecutor { // Check note inputs // ---------------------------------------------------------------------------------------- - let note_ids = notes.iter().map(|input_note| input_note.id()).collect::>(); - - let tx_inputs = - maybe_await!(self.data_store.get_transaction_inputs(account_id, block_ref, ¬e_ids)) - .map_err(TransactionExecutorError::FetchTransactionInputsFailed)?; - - let input_notes = tx_inputs.input_notes().clone().into_vec(); - - for note in input_notes.iter() { + for note in notes.iter() { if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { let inputs_check_result = - well_known_note.check_note_inputs(note.note(), tx_inputs.account()); + well_known_note.check_note_inputs(note.note(), account_id); if let NoteAccountCompatibility::No = inputs_check_result { return Ok(ExecutionCheckResult::Failure((note.id(), vec![]))); } @@ -287,7 +279,7 @@ impl TransactionExecutor { // Execute transaction // ---------------------------------------------------------------------------------------- - maybe_await!(self.notes_execution_progress_checker(tx_inputs, tx_args)) + maybe_await!(self.notes_execution_progress_checker(account_id, block_ref, notes, tx_args)) } /// Executes the transaction with specified notes, returning the [ExecutionCheckResult::Success] @@ -302,9 +294,17 @@ impl TransactionExecutor { #[maybe_async] fn notes_execution_progress_checker( &self, - tx_inputs: TransactionInputs, + account_id: AccountId, + block_ref: BlockNumber, + notes: &InputNotes, tx_args: TransactionArgs, ) -> Result { + let note_ids = notes.iter().map(|input_note| input_note.id()).collect::>(); + + let tx_inputs = + maybe_await!(self.data_store.get_transaction_inputs(account_id, block_ref, ¬e_ids)) + .map_err(TransactionExecutorError::FetchTransactionInputsFailed)?; + let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&tx_inputs, &tx_args, None); let advice_recorder: RecAdviceProvider = advice_inputs.into(); From 2b9f5f459713f493ee040d863daa4d7a1843a3da Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Tue, 8 Apr 2025 15:58:25 +0300 Subject: [PATCH 07/18] feat: impl NotesChecker and move check procs there --- crates/miden-tx/src/executor/mod.rs | 58 ++----------- crates/miden-tx/src/executor/notes_checker.rs | 83 +++++++++++++++++++ crates/miden-tx/src/tests/mod.rs | 61 +++++++++++--- 3 files changed, 141 insertions(+), 61 deletions(-) create mode 100644 crates/miden-tx/src/executor/notes_checker.rs diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index ddfedca652..7e83b24f68 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -1,19 +1,13 @@ use alloc::{collections::BTreeSet, sync::Arc, vec::Vec}; -use miden_lib::{ - account::interface::NoteAccountCompatibility, note::well_known_note::WellKnownNote, - transaction::TransactionKernel, -}; +use miden_lib::transaction::TransactionKernel; use miden_objects::{ Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES, ZERO, account::{AccountCode, AccountId}, assembly::Library, block::BlockNumber, note::NoteId, - transaction::{ - ExecutedTransaction, InputNote, InputNotes, TransactionArgs, TransactionInputs, - TransactionScript, - }, + transaction::{ExecutedTransaction, TransactionArgs, TransactionInputs, TransactionScript}, vm::StackOutputs, }; use vm_processor::{AdviceInputs, ExecutionOptions, Process, RecAdviceProvider}; @@ -28,6 +22,9 @@ pub use data_store::DataStore; mod mast_store; pub use mast_store::TransactionMastStore; +mod notes_checker; +pub use notes_checker::NotesChecker; + // TRANSACTION EXECUTOR // ================================================================================================ @@ -245,43 +242,6 @@ impl TransactionExecutor { // CHECK CONSUMABILITY // ============================================================================================ - /// Checks whether the provided input notes could be consumed by the provided account. - /// - /// This check consists of two main steps: - /// - Check whether there are "well known" notes (`P2ID`, `P2IDR` and `SWAP`) in the list of the - /// provided input notes. If so, assert that the note inputs are correct. - /// - Execute the transaction with specified notes. - /// - Returns [`ExecutionCheckResult::Success`] if the execution was successful. - /// - Returns [`ExecutionCheckResult::Failure`] if some note returned an error. The tuple - /// associated with `Failure` variant contains the ID of the failing note and a vector of - /// IDs of the notes, which were successfully executed. - #[maybe_async] - pub fn check_notes_consumability( - &self, - account_id: AccountId, - block_ref: BlockNumber, - notes: &InputNotes, - tx_args: TransactionArgs, - ) -> Result { - // Check note inputs - // ---------------------------------------------------------------------------------------- - - for note in notes.iter() { - if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { - let inputs_check_result = - well_known_note.check_note_inputs(note.note(), account_id); - if let NoteAccountCompatibility::No = inputs_check_result { - return Ok(ExecutionCheckResult::Failure((note.id(), vec![]))); - } - } - } - - // Execute transaction - // ---------------------------------------------------------------------------------------- - - maybe_await!(self.notes_execution_progress_checker(account_id, block_ref, notes, tx_args)) - } - /// Executes the transaction with specified notes, returning the [ExecutionCheckResult::Success] /// if all notes has been consumed successfully and [ExecutionCheckResult::Failure] if some note /// returned an error. @@ -292,17 +252,15 @@ impl TransactionExecutor { /// - If the transaction host can not be created from the provided values. /// - If the execution of the provided program fails on the stage other than note execution. #[maybe_async] - fn notes_execution_progress_checker( + pub(crate) fn notes_execution_progress_checker( &self, account_id: AccountId, block_ref: BlockNumber, - notes: &InputNotes, + note_ids: &[NoteId], tx_args: TransactionArgs, ) -> Result { - let note_ids = notes.iter().map(|input_note| input_note.id()).collect::>(); - let tx_inputs = - maybe_await!(self.data_store.get_transaction_inputs(account_id, block_ref, ¬e_ids)) + maybe_await!(self.data_store.get_transaction_inputs(account_id, block_ref, note_ids)) .map_err(TransactionExecutorError::FetchTransactionInputsFailed)?; let (stack_inputs, advice_inputs) = diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs new file mode 100644 index 0000000000..478ca3f162 --- /dev/null +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -0,0 +1,83 @@ +use alloc::vec::Vec; + +use miden_lib::{ + account::interface::NoteAccountCompatibility, note::well_known_note::WellKnownNote, +}; +use miden_objects::{ + account::AccountId, + block::BlockNumber, + note::NoteId, + transaction::{InputNote, TransactionArgs}, +}; +use winter_maybe_async::{maybe_async, maybe_await}; + +use super::{ExecutionCheckResult, TransactionExecutor}; +use crate::TransactionExecutorError; + +pub struct NotesChecker { + account_id: AccountId, + notes: Vec, +} + +impl NotesChecker { + pub fn new(account_id: AccountId, notes: Vec) -> Self { + NotesChecker { account_id, notes } + } + + /// Checks whether there are "well known" notes (`P2ID`, `P2IDR` and `SWAP`) in the list of the + /// provided input notes. If so, assert that the note inputs are correct. + /// + /// Returns [NoteAccountCompatibility::No] if at least one note has incorrect inputs. + pub fn check_note_inputs(&self) -> (NoteAccountCompatibility, Option) { + for note in self.notes.iter() { + if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { + if let NoteAccountCompatibility::No = + well_known_note.check_note_inputs(note.note(), self.account_id) + { + return (NoteAccountCompatibility::No, Some(note.id())); + } + } + } + + (NoteAccountCompatibility::Maybe, None) + } + + /// Checks whether the provided input notes could be consumed by the provided account. + /// + /// This check consists of two main steps: + /// - Check whether there are "well known" notes (`P2ID`, `P2IDR` and `SWAP`) in the list of the + /// provided input notes. If so, assert that the note inputs are correct. + /// - Execute the transaction with specified notes. + /// - Returns [`ExecutionCheckResult::Success`] if the execution was successful. + /// - Returns [`ExecutionCheckResult::Failure`] if some note returned an error. The tuple + /// associated with `Failure` variant contains the ID of the failing note and a vector of + /// IDs of the notes, which were successfully executed. + #[maybe_async] + pub fn check_notes_consumability( + &self, + tx_executor: &TransactionExecutor, + block_ref: BlockNumber, + tx_args: TransactionArgs, + ) -> Result { + // Check input notes + // ---------------------------------------------------------------------------------------- + + let inputs_check_result = self.check_note_inputs(); + if let (NoteAccountCompatibility::No, failing_note_id) = inputs_check_result { + return Ok(ExecutionCheckResult::Failure(( + failing_note_id.expect("tuple with incompatible note should contain its ID"), + vec![], + ))); + } + + // Execute transaction + // ---------------------------------------------------------------------------------------- + let note_ids = self.notes.iter().map(|note| note.id()).collect::>(); + maybe_await!(tx_executor.notes_execution_progress_checker( + self.account_id, + block_ref, + ¬e_ids, + tx_args + )) + } +} diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index 63dab08681..148dbd436a 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -9,7 +9,10 @@ use ::assembly::{ LibraryPath, ast::{Module, ModuleKind}, }; -use miden_lib::transaction::TransactionKernel; +use miden_lib::{ + account::interface::NoteAccountCompatibility, note::create_p2id_note, + transaction::TransactionKernel, +}; use miden_objects::{ Felt, MIN_PROOF_SECURITY_LEVEL, Word, account::{AccountBuilder, AccountComponent, AccountId, AccountStorage, StorageSlot}, @@ -23,13 +26,14 @@ use miden_objects::{ account_component::AccountMockComponent, account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, ACCOUNT_ID_SENDER, + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, ACCOUNT_ID_SENDER, }, constants::{FUNGIBLE_ASSET_AMOUNT, NON_FUNGIBLE_ASSET_DATA}, note::{DEFAULT_NOTE_CODE, NoteBuilder}, storage::{STORAGE_INDEX_0, STORAGE_INDEX_2}, }, - transaction::{ProvenTransaction, TransactionArgs, TransactionScript}, + transaction::{InputNote, ProvenTransaction, TransactionArgs, TransactionScript}, utils::word_to_masm_push_string, }; use miden_prover::ProvingOptions; @@ -37,6 +41,7 @@ use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; use vm_processor::{ Digest, MemAdviceProvider, ONE, + crypto::RpoRandomCoin, utils::{Deserializable, Serializable}, }; @@ -45,7 +50,9 @@ use super::{ TransactionVerifier, }; use crate::{ - TransactionMastStore, executor::ExecutionCheckResult, testing::TransactionContextBuilder, + TransactionMastStore, + executor::{ExecutionCheckResult, NotesChecker}, + testing::TransactionContextBuilder, }; mod kernel_tests; @@ -1050,6 +1057,38 @@ fn test_execute_program() { assert_eq!(stack_outputs[..3], [Felt::new(7), Felt::new(2), ONE]); } +#[test] +fn check_note_inputs() { + let sender_account_id = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap(); + let target_account_id = + AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE).unwrap(); + + let p2id_note = create_p2id_note( + sender_account_id, + target_account_id, + vec![], + NoteType::Public, + Default::default(), + &mut RpoRandomCoin::new([ONE, Felt::new(2), Felt::new(3), Felt::new(4)]), + ) + .expect("failed to create P2ID note"); + let p2id_note_id = p2id_note.id(); + + // Success + // -------------------------------------------------------------------------------------------- + let notes_checker = + NotesChecker::new(target_account_id, vec![InputNote::unauthenticated(p2id_note.clone())]); + let note_inputs_check_result = notes_checker.check_note_inputs(); + assert_eq!(note_inputs_check_result, (NoteAccountCompatibility::Maybe, None)); + + // Failure + // -------------------------------------------------------------------------------------------- + let notes_checker = + NotesChecker::new(sender_account_id, vec![InputNote::unauthenticated(p2id_note)]); + let note_inputs_check_result = notes_checker.check_note_inputs(); + assert_eq!(note_inputs_check_result, (NoteAccountCompatibility::No, Some(p2id_note_id))); +} + #[test] fn test_check_note_consumability() { // Success @@ -1065,11 +1104,11 @@ fn test_check_note_consumability() { let executor: TransactionExecutor = TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); - let execution_check_result: ExecutionCheckResult = executor - .check_notes_consumability(account_id, block_ref, input_notes, tx_context.tx_args().clone()) + let notes_checker = NotesChecker::new(account_id, input_notes.clone().into_vec()); + let execution_check_result = notes_checker + .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) .unwrap(); - - assert_eq!(execution_check_result, ExecutionCheckResult::Success,); + assert_eq!(execution_check_result, ExecutionCheckResult::Success); // Failure // -------------------------------------------------------------------------------------------- @@ -1105,10 +1144,10 @@ fn test_check_note_consumability() { let executor: TransactionExecutor = TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); - let execution_check_result: ExecutionCheckResult = executor - .check_notes_consumability(account_id, block_ref, input_notes, tx_context.tx_args().clone()) + let notes_checker = NotesChecker::new(account_id, input_notes.clone().into_vec()); + let execution_check_result = notes_checker + .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) .unwrap(); - assert_eq!( execution_check_result, ExecutionCheckResult::Failure((failing_note_2.id(), vec![input_note_ids[0]])) From 9a6b0e6bc6c1781e053b4a40b02e43074d9ca231 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Thu, 10 Apr 2025 18:47:46 +0300 Subject: [PATCH 08/18] chore: export NoteChecker --- Cargo.lock | 240 ++++++++++++++++++------------------- crates/miden-tx/src/lib.rs | 2 +- 2 files changed, 115 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0b644b442..15d2df662b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,17 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.15", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -138,9 +127,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" dependencies = [ "backtrace", ] @@ -263,11 +252,11 @@ dependencies = [ [[package]] name = "axum" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" +checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288" dependencies = [ - "axum-core 0.5.0", + "axum-core 0.5.2", "bytes", "form_urlencoded", "futures-util", @@ -317,12 +306,12 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ "bytes", - "futures-util", + "futures-core", "http", "http-body", "http-body-util", @@ -415,9 +404,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17679a8d69b6d7fd9cd9801a536cec9fa5e5970b69f9d4747f70b39b031f5e7" +checksum = "389a099b34312839e16420d499a9cad9650541715937ffbdd40d36f49e77eeb3" dependencies = [ "arrayref", "arrayvec", @@ -488,9 +477,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.17" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ "jobserver", "libc", @@ -558,9 +547,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", "clap_derive 4.5.32", @@ -568,9 +557,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", @@ -682,7 +671,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.32", + "clap 4.5.36", "criterion-plot", "is-terminal", "itertools 0.10.5", @@ -711,9 +700,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -904,9 +893,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -944,9 +933,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "libz-ng-sys", @@ -1146,9 +1135,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "h2" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" dependencies = [ "atomic-waker", "bytes", @@ -1156,7 +1145,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.8.0", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -1165,9 +1154,9 @@ dependencies = [ [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -1178,9 +1167,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] [[package]] name = "hashbrown" @@ -1352,9 +1338,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -1362,6 +1348,7 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -1410,9 +1397,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -1434,9 +1421,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -1455,9 +1442,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -1526,9 +1513,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1540,8 +1527,8 @@ version = "0.11.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" dependencies = [ - "ahash 0.8.11", - "indexmap 2.8.0", + "ahash", + "indexmap 2.9.0", "is-terminal", "itoa", "log", @@ -1616,10 +1603,11 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -1721,9 +1709,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" @@ -1743,9 +1731,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "logos" @@ -1795,9 +1783,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" +checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" dependencies = [ "hashbrown 0.15.2", ] @@ -1855,9 +1843,9 @@ dependencies = [ [[package]] name = "miden-air" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72269e041e915d6c34325a8906f08a8ff6ec9b92c79ae07f3a0de8c3588694b5" +checksum = "00e2e77c57ae798e02553af158c04d467f6479ab798a8c84459d343a89ff9c50" dependencies = [ "miden-core", "thiserror 2.0.12", @@ -1867,9 +1855,9 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce5680a0e75470389b65cc00900422ad7e1aa7972477c75ff88f63c3a01671e9" +checksum = "75d1cb02d807c2481f365feca966bedb74c66c765923d04c3c23ea678afaf148" dependencies = [ "aho-corasick", "lalrpop", @@ -1913,9 +1901,9 @@ dependencies = [ [[package]] name = "miden-core" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ba125a31e9ec0e732f47e639525c753973e553126cfc46cc63674049ea4134" +checksum = "d7d408e01421b5df2e4cfaf1a91efefe3fb5b729f1c99b668d3a69eec0044a6b" dependencies = [ "lock_api", "loom", @@ -2046,9 +2034,9 @@ dependencies = [ [[package]] name = "miden-processor" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5f362138a7bfe6c20246de651e848566843a0d5062cc4a7a8ebff6bd14668d" +checksum = "d4e70b053c587014396d06be3502e3952777923bee11729b7514c060fdc0d2c8" dependencies = [ "miden-air", "miden-core", @@ -2059,9 +2047,9 @@ dependencies = [ [[package]] name = "miden-prover" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6531711c8f3e25be9c607f82fc456594aba061acd3cde1df5d23eed88579340" +checksum = "2baa571449e7811e934dae919285dfea4854fb867f6d20d757d60b0601ef0140" dependencies = [ "miden-air", "miden-processor", @@ -2075,9 +2063,9 @@ name = "miden-proving-service" version = "0.9.0" dependencies = [ "async-trait", - "axum 0.8.1", + "axum 0.8.3", "bytes", - "clap 4.5.32", + "clap 4.5.36", "miden-block-prover", "miden-lib", "miden-objects", @@ -2134,9 +2122,9 @@ dependencies = [ [[package]] name = "miden-stdlib" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "116d30a8db5167f88944509007b8bb7aad4fa0d9d03f2610b4e80be3a198ad37" +checksum = "77e59c4dd1079bffe8407694b386ae781feaf1f7acb9d1cefa858578bc99cad4" dependencies = [ "miden-assembly", "miden-core", @@ -2180,9 +2168,9 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ae6636d1e7a9d78a973fd59ffcfd87964eee235ecdf3264569add5ff89b0d91" +checksum = "20ae024b022569d7117891a83cf51ccd5bceeda248f30690b394a6dfc4607061" dependencies = [ "miden-air", "miden-core", @@ -2230,9 +2218,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -2416,9 +2404,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" @@ -2428,9 +2416,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "openssl" -version = "0.10.71" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ "bitflags 2.9.0", "cfg-if", @@ -2460,9 +2448,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.106" +version = "0.9.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" dependencies = [ "cc", "libc", @@ -2472,9 +2460,9 @@ dependencies = [ [[package]] name = "opentelemetry" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "768ee97dc5cd695a4dd4a69a0678fb42789666b5a89e8c0af48bb06c6e427120" +checksum = "9e87237e2775f74896f9ad219d26a2081751187eb7c9f5c58dde20a23b95d16c" dependencies = [ "futures-core", "futures-sink", @@ -2616,7 +2604,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.8.0", + "indexmap 2.9.0", ] [[package]] @@ -2626,7 +2614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset 0.5.7", - "indexmap 2.8.0", + "indexmap 2.9.0", ] [[package]] @@ -2689,7 +2677,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35ee62f28526d8d484621e77f8d6a1807f1bd07558a06ab5a204b4834d6be056" dependencies = [ - "ahash 0.8.11", + "ahash", "async-trait", "blake2", "bytes", @@ -2724,7 +2712,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d123320b69bd06e897fc16bd1dde962a7b488c4d2ae825683fbca0198fad8669" dependencies = [ - "ahash 0.8.11", + "ahash", "async-trait", "brotli", "bytes", @@ -2815,7 +2803,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfcc8e3afeae5a83bbcd415d8d3bb50bea31d2eda2a91f79220b59abab86dd0f" dependencies = [ - "ahash 0.8.11", + "ahash", ] [[package]] @@ -2847,7 +2835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb50f65f06c4b81ccb3edcceaa54bb9439608506b0b3b8c048798169a64aad8e" dependencies = [ "arrayvec", - "hashbrown 0.12.3", + "hashbrown 0.15.2", "parking_lot", "rand 0.9.0", ] @@ -2989,9 +2977,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", "syn 2.0.100", @@ -3267,9 +3255,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags 2.9.0", ] @@ -3457,9 +3445,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.37.0" +version = "1.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c24af6e7ac43c88a8a458d1139d0246fdce2f6cd2f1ac6cb51eb88b29c978af" +checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" dependencies = [ "arrayvec", "num-traits", @@ -3504,22 +3492,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.25" +version = "0.23.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" dependencies = [ "once_cell", "rustls-pki-types", @@ -3545,9 +3533,9 @@ checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" -version = "0.103.0" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa4eeac2588ffff23e9d7a7e9b3f971c5fb5b7ebc9452745e0c232c64f83b2f" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ "ring", "rustls-pki-types", @@ -3696,7 +3684,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "itoa", "memchr", "ryu", @@ -3763,7 +3751,7 @@ version = "0.9.4" source = "git+https://github.com/undef1nd/sfv.git?tag=v0.9.4#e6499d8ce11271dd0a4c1f72445775a58b835a56" dependencies = [ "data-encoding", - "indexmap 2.8.0", + "indexmap 2.9.0", "rust_decimal", ] @@ -3818,9 +3806,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smawk" @@ -3830,9 +3818,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3858,9 +3846,9 @@ checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" [[package]] name = "string_cache" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot", @@ -3940,9 +3928,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "symbolic-common" -version = "12.14.1" +version = "12.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66135c8273581acaab470356f808a1c74a707fe7ec24728af019d7247e089e71" +checksum = "cb426702a1ee7c1d2ebf3b6fac2e67fde84f6d6396e581826e3f055d1bffb2a4" dependencies = [ "debugid", "memmap2", @@ -3952,9 +3940,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.14.1" +version = "12.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bcacd080282a72e795864660b148392af7babd75691d5ae9a3b77e29c98c77" +checksum = "d4671b7ae11875cb9c34348d2df6c5d1edd51f4c98ec45f591acb593ac8af8e0" dependencies = [ "rustc-demangle", "symbolic-common", @@ -4038,7 +4026,7 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix 1.0.3", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -4078,7 +4066,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix 1.0.3", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -4184,9 +4172,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.1" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -4295,7 +4283,7 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -5207,9 +5195,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] diff --git a/crates/miden-tx/src/lib.rs b/crates/miden-tx/src/lib.rs index d50a3a2407..49b0fc2b71 100644 --- a/crates/miden-tx/src/lib.rs +++ b/crates/miden-tx/src/lib.rs @@ -9,7 +9,7 @@ extern crate std; pub use miden_objects::transaction::TransactionInputs; mod executor; -pub use executor::{DataStore, TransactionExecutor, TransactionMastStore}; +pub use executor::{DataStore, NotesChecker, TransactionExecutor, TransactionMastStore}; pub mod host; pub use host::{TransactionHost, TransactionProgress}; From 4ab22304d91a9cad30103782c42a77ba169d0fdd Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Tue, 15 Apr 2025 00:13:56 +0300 Subject: [PATCH 09/18] refactor: update names, add docs, add error to NoteAccountExecution --- crates/miden-tx/src/executor/mod.rs | 49 +++++++++++-------- crates/miden-tx/src/executor/notes_checker.rs | 38 +++++++++----- crates/miden-tx/src/host/tx_progress.rs | 8 ++- crates/miden-tx/src/lib.rs | 4 +- crates/miden-tx/src/tests/mod.rs | 33 ++++++++----- 5 files changed, 83 insertions(+), 49 deletions(-) diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 7e83b24f68..7a51e87479 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -10,7 +10,7 @@ use miden_objects::{ transaction::{ExecutedTransaction, TransactionArgs, TransactionInputs, TransactionScript}, vm::StackOutputs, }; -use vm_processor::{AdviceInputs, ExecutionOptions, Process, RecAdviceProvider}; +use vm_processor::{AdviceInputs, ExecutionOptions, MemAdviceProvider, Process, RecAdviceProvider}; use winter_maybe_async::{maybe_async, maybe_await}; use super::{TransactionExecutorError, TransactionHost}; @@ -23,7 +23,7 @@ mod mast_store; pub use mast_store::TransactionMastStore; mod notes_checker; -pub use notes_checker::NotesChecker; +pub use notes_checker::{NoteInputsCheck, NotesChecker}; // TRANSACTION EXECUTOR // ================================================================================================ @@ -252,27 +252,27 @@ impl TransactionExecutor { /// - If the transaction host can not be created from the provided values. /// - If the execution of the provided program fails on the stage other than note execution. #[maybe_async] - pub(crate) fn notes_execution_progress_checker( + pub(crate) fn try_notes_execution( &self, account_id: AccountId, block_ref: BlockNumber, note_ids: &[NoteId], tx_args: TransactionArgs, - ) -> Result { + ) -> Result { let tx_inputs = maybe_await!(self.data_store.get_transaction_inputs(account_id, block_ref, note_ids)) .map_err(TransactionExecutorError::FetchTransactionInputsFailed)?; let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&tx_inputs, &tx_args, None); - let advice_recorder: RecAdviceProvider = advice_inputs.into(); + let advice_provider: MemAdviceProvider = advice_inputs.into(); // load note script MAST into the MAST store self.mast_store.load_transaction_code(&tx_inputs, &tx_args); let mut host = TransactionHost::new( tx_inputs.account().into(), - advice_recorder, + advice_provider, self.mast_store.clone(), self.authenticator.clone(), self.account_codes.iter().map(|code| code.commitment()).collect(), @@ -289,28 +289,31 @@ impl TransactionExecutor { .map_err(TransactionExecutorError::TransactionProgramExecutionFailed); match result { - Ok(_) => Ok(ExecutionCheckResult::Success), - Err(e) => { + Ok(_) => Ok(NoteAccountExecution::Success), + Err(tx_execution_error) => { let notes = host.tx_progress().note_execution(); // empty notes vector means that we didn't process the notes, so an error // occurred somewhere else if notes.is_empty() { - return Err(e); + return Err(tx_execution_error); } - let (last, success_notes) = notes.split_last().expect("notes vector is empty"); + let ((last_note, last_note_interval), success_notes) = notes + .split_last() + .expect("notes vector should not be empty because we just checked"); // if the interval end of the last note is specified, then an error occurred after // notes processing - if last.1.end().is_some() { - return Err(e); + if last_note_interval.end().is_some() { + return Err(tx_execution_error); } - Ok(ExecutionCheckResult::Failure(( - last.0, - success_notes.iter().map(|note| note.0).collect(), - ))) + Ok(NoteAccountExecution::Failure { + failed_note_id: *last_note, + successful_notes: success_notes.iter().map(|(note, _)| *note).collect(), + error: Some(tx_execution_error), + }) }, } } @@ -380,10 +383,14 @@ fn build_executed_transaction( /// Describes whether a transaction with a specified set of notes could be executed against target /// account. /// -/// [ExecutionCheckResult::Failure] holds a tuple, the first value of which is a failing note ID, -/// and the second is a vector of note IDs which were successfully executed. -#[derive(Debug, PartialEq)] -pub enum ExecutionCheckResult { +/// [NoteAccountExecution::Failure] holds data for error handling: `failing_note_id` is an ID of a +/// failing note and `successful_notes` is a vector of note IDs which were successfully executed. +#[derive(Debug)] +pub enum NoteAccountExecution { Success, - Failure((NoteId, Vec)), + Failure { + failed_note_id: NoteId, + successful_notes: Vec, + error: Option, + }, } diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index 478ca3f162..00c23cb3a7 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -11,15 +11,22 @@ use miden_objects::{ }; use winter_maybe_async::{maybe_async, maybe_await}; -use super::{ExecutionCheckResult, TransactionExecutor}; +use super::{NoteAccountExecution, TransactionExecutor}; use crate::TransactionExecutorError; +/// This struct performs input notes checks against provided target account. +/// +/// A check could be: +/// - Static -- performed by the [NotesChecker::check_note_inputs]. See method description. +/// - Dynamic -- performed by the [NotesChecker::check_notes_consumability]. Essentially runs the +/// transaction to make sure that provided input notes could be consumed by the account. pub struct NotesChecker { account_id: AccountId, notes: Vec, } impl NotesChecker { + /// Returns a new instance of the [NotesChecker]. pub fn new(account_id: AccountId, notes: Vec) -> Self { NotesChecker { account_id, notes } } @@ -28,18 +35,18 @@ impl NotesChecker { /// provided input notes. If so, assert that the note inputs are correct. /// /// Returns [NoteAccountCompatibility::No] if at least one note has incorrect inputs. - pub fn check_note_inputs(&self) -> (NoteAccountCompatibility, Option) { + pub fn check_note_inputs(&self) -> NoteInputsCheck { for note in self.notes.iter() { if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { if let NoteAccountCompatibility::No = well_known_note.check_note_inputs(note.note(), self.account_id) { - return (NoteAccountCompatibility::No, Some(note.id())); + return NoteInputsCheck::No { failed_note_id: note.id() }; } } } - (NoteAccountCompatibility::Maybe, None) + NoteInputsCheck::Maybe } /// Checks whether the provided input notes could be consumed by the provided account. @@ -58,22 +65,22 @@ impl NotesChecker { tx_executor: &TransactionExecutor, block_ref: BlockNumber, tx_args: TransactionArgs, - ) -> Result { + ) -> Result { // Check input notes // ---------------------------------------------------------------------------------------- - let inputs_check_result = self.check_note_inputs(); - if let (NoteAccountCompatibility::No, failing_note_id) = inputs_check_result { - return Ok(ExecutionCheckResult::Failure(( - failing_note_id.expect("tuple with incompatible note should contain its ID"), - vec![], - ))); + if let NoteInputsCheck::No { failed_note_id } = inputs_check_result { + return Ok(NoteAccountExecution::Failure { + failed_note_id, + successful_notes: vec![], + error: None, + }); } // Execute transaction // ---------------------------------------------------------------------------------------- let note_ids = self.notes.iter().map(|note| note.id()).collect::>(); - maybe_await!(tx_executor.notes_execution_progress_checker( + maybe_await!(tx_executor.try_notes_execution( self.account_id, block_ref, ¬e_ids, @@ -81,3 +88,10 @@ impl NotesChecker { )) } } + +/// Helper enum for getting a result of the well known note inputs check. +#[derive(Debug, PartialEq)] +pub enum NoteInputsCheck { + Maybe, + No { failed_note_id: NoteId }, +} diff --git a/crates/miden-tx/src/host/tx_progress.rs b/crates/miden-tx/src/host/tx_progress.rs index 1d818b36ef..1c7fe2d62a 100644 --- a/crates/miden-tx/src/host/tx_progress.rs +++ b/crates/miden-tx/src/host/tx_progress.rs @@ -132,8 +132,12 @@ impl CycleInterval { self.end = Some(e); } - pub fn end(&self) -> &Option { - &self.end + pub fn start(&self) -> Option { + self.start + } + + pub fn end(&self) -> Option { + self.end } /// Calculate the length of the interval diff --git a/crates/miden-tx/src/lib.rs b/crates/miden-tx/src/lib.rs index 49b0fc2b71..1706545b34 100644 --- a/crates/miden-tx/src/lib.rs +++ b/crates/miden-tx/src/lib.rs @@ -9,7 +9,9 @@ extern crate std; pub use miden_objects::transaction::TransactionInputs; mod executor; -pub use executor::{DataStore, NotesChecker, TransactionExecutor, TransactionMastStore}; +pub use executor::{ + DataStore, NoteInputsCheck, NotesChecker, TransactionExecutor, TransactionMastStore, +}; pub mod host; pub use host::{TransactionHost, TransactionProgress}; diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index 148dbd436a..c53ac3a102 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -9,10 +9,7 @@ use ::assembly::{ LibraryPath, ast::{Module, ModuleKind}, }; -use miden_lib::{ - account::interface::NoteAccountCompatibility, note::create_p2id_note, - transaction::TransactionKernel, -}; +use miden_lib::{note::create_p2id_note, transaction::TransactionKernel}; use miden_objects::{ Felt, MIN_PROOF_SECURITY_LEVEL, Word, account::{AccountBuilder, AccountComponent, AccountId, AccountStorage, StorageSlot}, @@ -50,8 +47,8 @@ use super::{ TransactionVerifier, }; use crate::{ - TransactionMastStore, - executor::{ExecutionCheckResult, NotesChecker}, + TransactionExecutorError, TransactionMastStore, + executor::{NoteAccountExecution, NoteInputsCheck, NotesChecker}, testing::TransactionContextBuilder, }; @@ -1079,14 +1076,14 @@ fn check_note_inputs() { let notes_checker = NotesChecker::new(target_account_id, vec![InputNote::unauthenticated(p2id_note.clone())]); let note_inputs_check_result = notes_checker.check_note_inputs(); - assert_eq!(note_inputs_check_result, (NoteAccountCompatibility::Maybe, None)); + assert_eq!(note_inputs_check_result, NoteInputsCheck::Maybe); // Failure // -------------------------------------------------------------------------------------------- let notes_checker = NotesChecker::new(sender_account_id, vec![InputNote::unauthenticated(p2id_note)]); let note_inputs_check_result = notes_checker.check_note_inputs(); - assert_eq!(note_inputs_check_result, (NoteAccountCompatibility::No, Some(p2id_note_id))); + assert_eq!(note_inputs_check_result, NoteInputsCheck::No { failed_note_id: p2id_note_id }); } #[test] @@ -1108,7 +1105,7 @@ fn test_check_note_consumability() { let execution_check_result = notes_checker .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) .unwrap(); - assert_eq!(execution_check_result, ExecutionCheckResult::Success); + assert!(matches!(execution_check_result, NoteAccountExecution::Success)); // Failure // -------------------------------------------------------------------------------------------- @@ -1148,8 +1145,18 @@ fn test_check_note_consumability() { let execution_check_result = notes_checker .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) .unwrap(); - assert_eq!( - execution_check_result, - ExecutionCheckResult::Failure((failing_note_2.id(), vec![input_note_ids[0]])) - ); + + assert!(if let NoteAccountExecution::Failure { + failed_note_id, + successful_notes, + error: Some(e), + } = execution_check_result + { + assert_eq!(failed_note_id, failing_note_2.id()); + assert_eq!(successful_notes, vec![input_note_ids[0]]); + assert!(matches!(e, TransactionExecutorError::TransactionProgramExecutionFailed { .. })); + true + } else { + false + }); } From 2b5bbf5479aa2f13de8fe8398a424e7c4143de6b Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Tue, 15 Apr 2025 15:01:07 +0300 Subject: [PATCH 10/18] docs: update doc comments --- crates/miden-tx/src/executor/mod.rs | 4 ++-- crates/miden-tx/src/executor/notes_checker.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 7a51e87479..db5c0e3462 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -242,8 +242,8 @@ impl TransactionExecutor { // CHECK CONSUMABILITY // ============================================================================================ - /// Executes the transaction with specified notes, returning the [ExecutionCheckResult::Success] - /// if all notes has been consumed successfully and [ExecutionCheckResult::Failure] if some note + /// Executes the transaction with specified notes, returning the [NoteAccountExecution::Success] + /// if all notes has been consumed successfully and [NoteAccountExecution::Failure] if some note /// returned an error. /// /// # Errors: diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index 00c23cb3a7..16634fc2ef 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -11,8 +11,7 @@ use miden_objects::{ }; use winter_maybe_async::{maybe_async, maybe_await}; -use super::{NoteAccountExecution, TransactionExecutor}; -use crate::TransactionExecutorError; +use super::{NoteAccountExecution, TransactionExecutor, TransactionExecutorError}; /// This struct performs input notes checks against provided target account. /// @@ -55,10 +54,11 @@ impl NotesChecker { /// - Check whether there are "well known" notes (`P2ID`, `P2IDR` and `SWAP`) in the list of the /// provided input notes. If so, assert that the note inputs are correct. /// - Execute the transaction with specified notes. - /// - Returns [`ExecutionCheckResult::Success`] if the execution was successful. - /// - Returns [`ExecutionCheckResult::Failure`] if some note returned an error. The tuple - /// associated with `Failure` variant contains the ID of the failing note and a vector of - /// IDs of the notes, which were successfully executed. + /// - Returns `NoteAccountExecution::Success` if the execution was successful. + /// - Returns `NoteAccountExecution::Failure` if some note returned an error. The fields + /// associated with `Failure` variant contains the ID of the failed note, a vector of IDs of + /// the notes, which were successfully executed, and the [TransactionExecutorError] if the + /// check failed durning the execution stage. #[maybe_async] pub fn check_notes_consumability( &self, From 63e6229cae2a74bbc9cddafe49052e2f2d3405d3 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Thu, 17 Apr 2025 22:44:29 +0300 Subject: [PATCH 11/18] refactor: remove check_note_inputs proc, update logic of check_notes_consumability --- crates/miden-tx/src/executor/notes_checker.rs | 58 +++++++----- crates/miden-tx/src/tests/mod.rs | 93 ++++++++++--------- 2 files changed, 84 insertions(+), 67 deletions(-) diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index 16634fc2ef..ea9eab79d1 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -30,24 +30,6 @@ impl NotesChecker { NotesChecker { account_id, notes } } - /// Checks whether there are "well known" notes (`P2ID`, `P2IDR` and `SWAP`) in the list of the - /// provided input notes. If so, assert that the note inputs are correct. - /// - /// Returns [NoteAccountCompatibility::No] if at least one note has incorrect inputs. - pub fn check_note_inputs(&self) -> NoteInputsCheck { - for note in self.notes.iter() { - if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { - if let NoteAccountCompatibility::No = - well_known_note.check_note_inputs(note.note(), self.account_id) - { - return NoteInputsCheck::No { failed_note_id: note.id() }; - } - } - } - - NoteInputsCheck::Maybe - } - /// Checks whether the provided input notes could be consumed by the provided account. /// /// This check consists of two main steps: @@ -68,13 +50,39 @@ impl NotesChecker { ) -> Result { // Check input notes // ---------------------------------------------------------------------------------------- - let inputs_check_result = self.check_note_inputs(); - if let NoteInputsCheck::No { failed_note_id } = inputs_check_result { - return Ok(NoteAccountExecution::Failure { - failed_note_id, - successful_notes: vec![], - error: None, - }); + + let mut successful_notes = vec![]; + for note in self.notes.iter() { + if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { + if let WellKnownNote::SWAP = well_known_note { + // if we encountered a SWAP note, then we have to execute the transaction + // anyway, so we can stop checking + break; + } else if let NoteAccountCompatibility::No = + well_known_note.check_note_inputs(note.note(), self.account_id) + { + // return a `Failure` with the vector of successfully checked `P2ID` and + // `P2IDR` notes if the check failed + return Ok(NoteAccountExecution::Failure { + failed_note_id: note.id(), + successful_notes, + error: None, + }); + } else { + // put the successfully checked `P2ID` or `P2IDR` note to the vector + successful_notes.push(note.id()); + } + } else { + // if we encountered not a well known note, then we have to execute the transaction + // anyway, so we can stop checking + break; + } + } + + // if all checked notes turned out to be either `P2ID` or `P2IDR` notes and all of them + // passed, then we could safely return the `Success` + if successful_notes.len() == self.notes.len() { + return Ok(NoteAccountExecution::Success); } // Execute transaction diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index c53ac3a102..24b874d1c3 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -9,12 +9,17 @@ use ::assembly::{ LibraryPath, ast::{Module, ModuleKind}, }; -use miden_lib::{note::create_p2id_note, transaction::TransactionKernel}; +use assert_matches::assert_matches; +use miden_lib::{ + note::{create_p2id_note, create_p2idr_note}, + transaction::TransactionKernel, +}; use miden_objects::{ Felt, MIN_PROOF_SECURITY_LEVEL, Word, account::{AccountBuilder, AccountComponent, AccountId, AccountStorage, StorageSlot}, assembly::DefaultSourceManager, asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}, + block::BlockNumber, note::{ Note, NoteAssets, NoteExecutionHint, NoteExecutionMode, NoteHeader, NoteId, NoteInputs, NoteMetadata, NoteRecipient, NoteScript, NoteTag, NoteType, @@ -30,14 +35,14 @@ use miden_objects::{ note::{DEFAULT_NOTE_CODE, NoteBuilder}, storage::{STORAGE_INDEX_0, STORAGE_INDEX_2}, }, - transaction::{InputNote, ProvenTransaction, TransactionArgs, TransactionScript}, + transaction::{ProvenTransaction, TransactionArgs, TransactionScript}, utils::word_to_masm_push_string, }; use miden_prover::ProvingOptions; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; use vm_processor::{ - Digest, MemAdviceProvider, ONE, + Digest, ExecutionError, MemAdviceProvider, ONE, crypto::RpoRandomCoin, utils::{Deserializable, Serializable}, }; @@ -48,7 +53,7 @@ use super::{ }; use crate::{ TransactionExecutorError, TransactionMastStore, - executor::{NoteAccountExecution, NoteInputsCheck, NotesChecker}, + executor::{NoteAccountExecution, NotesChecker}, testing::TransactionContextBuilder, }; @@ -1055,40 +1060,48 @@ fn test_execute_program() { } #[test] -fn check_note_inputs() { - let sender_account_id = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap(); - let target_account_id = - AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE).unwrap(); - +fn test_check_note_consumability() { + // Success (well known notes) + // -------------------------------------------------------------------------------------------- let p2id_note = create_p2id_note( - sender_account_id, - target_account_id, - vec![], + 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 RpoRandomCoin::new([ONE, Felt::new(2), Felt::new(3), Felt::new(4)]), ) - .expect("failed to create P2ID note"); - let p2id_note_id = p2id_note.id(); + .unwrap(); - // Success - // -------------------------------------------------------------------------------------------- - let notes_checker = - NotesChecker::new(target_account_id, vec![InputNote::unauthenticated(p2id_note.clone())]); - let note_inputs_check_result = notes_checker.check_note_inputs(); - assert_eq!(note_inputs_check_result, NoteInputsCheck::Maybe); + let p2idr_note = create_p2idr_note( + 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(), + BlockNumber::default(), + &mut RpoRandomCoin::new([ONE, Felt::new(2), Felt::new(3), Felt::new(4)]), + ) + .unwrap(); - // Failure - // -------------------------------------------------------------------------------------------- - let notes_checker = - NotesChecker::new(sender_account_id, vec![InputNote::unauthenticated(p2id_note)]); - let note_inputs_check_result = notes_checker.check_note_inputs(); - assert_eq!(note_inputs_check_result, NoteInputsCheck::No { failed_note_id: p2id_note_id }); -} + let tx_context = TransactionContextBuilder::with_standard_account(ONE) + .input_notes(vec![p2id_note, p2idr_note]) + .build(); -#[test] -fn test_check_note_consumability() { - // Success + let input_notes = tx_context.input_notes(); + let account_id = tx_context.account().id(); + let block_ref = tx_context.tx_inputs().block_header().block_num(); + + let executor: TransactionExecutor = + TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); + + let notes_checker = NotesChecker::new(account_id, input_notes.clone().into_vec()); + let execution_check_result = notes_checker + .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) + .unwrap(); + assert_matches!(execution_check_result, NoteAccountExecution::Success); + + // Success (custom notes) // -------------------------------------------------------------------------------------------- let tx_context = TransactionContextBuilder::with_standard_account(ONE) .with_mock_notes_preserved() @@ -1105,7 +1118,7 @@ fn test_check_note_consumability() { let execution_check_result = notes_checker .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) .unwrap(); - assert!(matches!(execution_check_result, NoteAccountExecution::Success)); + assert_matches!(execution_check_result, NoteAccountExecution::Success); // Failure // -------------------------------------------------------------------------------------------- @@ -1146,17 +1159,13 @@ fn test_check_note_consumability() { .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) .unwrap(); - assert!(if let NoteAccountExecution::Failure { + assert_matches!(execution_check_result, NoteAccountExecution::Failure { failed_note_id, successful_notes, - error: Some(e), - } = execution_check_result - { - assert_eq!(failed_note_id, failing_note_2.id()); - assert_eq!(successful_notes, vec![input_note_ids[0]]); - assert!(matches!(e, TransactionExecutorError::TransactionProgramExecutionFailed { .. })); - true - } else { - false - }); + error: Some(e)} => { + assert_eq!(failed_note_id, failing_note_2.id()); + assert_eq!(successful_notes, vec![input_note_ids[0]]); + assert_matches!(e, TransactionExecutorError::TransactionProgramExecutionFailed(ExecutionError::DivideByZero(_))); + } + ); } From c3a7817ea7abb9c76b34b177f161e9b1400047d7 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Thu, 17 Apr 2025 23:11:58 +0300 Subject: [PATCH 12/18] chore: update doc comments --- crates/miden-tx/src/executor/notes_checker.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index ea9eab79d1..01bbe52b7a 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -13,12 +13,11 @@ use winter_maybe_async::{maybe_async, maybe_await}; use super::{NoteAccountExecution, TransactionExecutor, TransactionExecutorError}; -/// This struct performs input notes checks against provided target account. +/// This struct performs input notes check against provided target account. /// -/// A check could be: -/// - Static -- performed by the [NotesChecker::check_note_inputs]. See method description. -/// - Dynamic -- performed by the [NotesChecker::check_notes_consumability]. Essentially runs the -/// transaction to make sure that provided input notes could be consumed by the account. +/// The check is performed using the [NotesChecker::check_notes_consumability] procedure. +/// Essentially runs the transaction to make sure that provided input notes could be consumed by the +/// account. pub struct NotesChecker { account_id: AccountId, notes: Vec, @@ -33,9 +32,9 @@ impl NotesChecker { /// Checks whether the provided input notes could be consumed by the provided account. /// /// This check consists of two main steps: - /// - Check whether there are "well known" notes (`P2ID`, `P2IDR` and `SWAP`) in the list of the - /// provided input notes. If so, assert that the note inputs are correct. - /// - Execute the transaction with specified notes. + /// - Statically check the notes: if all notes are either `P2ID` or `P2IDR` notes with correct + /// inputs, return `NoteAccountExecution::Success`. + /// - Execute the transaction: /// - Returns `NoteAccountExecution::Success` if the execution was successful. /// - Returns `NoteAccountExecution::Failure` if some note returned an error. The fields /// associated with `Failure` variant contains the ID of the failed note, a vector of IDs of From d060511e131696a372ca51472f8fea87c1fc7a5b Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Tue, 22 Apr 2025 13:57:38 +0300 Subject: [PATCH 13/18] refactor: add recall block hight check, add Yes to NoteAccountCompatibility --- crates/miden-lib/src/account/interface/mod.rs | 2 + crates/miden-lib/src/note/well_known_note.rs | 43 +++++++------ crates/miden-tx/src/executor/mod.rs | 4 +- crates/miden-tx/src/executor/notes_checker.rs | 61 ++++++++----------- crates/miden-tx/src/lib.rs | 2 +- crates/miden-tx/src/tests/mod.rs | 34 ++++++++--- 6 files changed, 83 insertions(+), 63 deletions(-) diff --git a/crates/miden-lib/src/account/interface/mod.rs b/crates/miden-lib/src/account/interface/mod.rs index df485ef71b..d00ba4fcc1 100644 --- a/crates/miden-lib/src/account/interface/mod.rs +++ b/crates/miden-lib/src/account/interface/mod.rs @@ -311,6 +311,8 @@ pub enum NoteAccountCompatibility { /// The account has all necessary procedures of one execution branch of the note script. This /// means the note may be able to be consumed by the account if that branch is executed. Maybe, + /// A note could be successfully executed and consumed by the account. + Yes, } // HELPER FUNCTIONS diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index cf34546c6b..61f8bc80cd 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -1,10 +1,5 @@ use miden_objects::{ - Digest, Felt, - account::AccountId, - assembly::{ProcedureName, QualifiedProcedureName}, - note::{Note, NoteScript}, - utils::{Deserializable, sync::LazyLock}, - vm::Program, + account::AccountId, assembly::{ProcedureName, QualifiedProcedureName}, block::BlockNumber, note::{Note, NoteScript}, utils::{sync::LazyLock, Deserializable}, vm::Program, Digest, Felt }; use crate::account::{ @@ -153,12 +148,11 @@ impl WellKnownNote { /// - for all notes: a check that note inputs have correct number of values. /// - for `P2ID` and `P2IDR` notes: assertion that the account ID provided by the note inputs is /// equal to the target account ID. - /// - for `SWAP` note: a check that the target account has sufficient amount of assets required - /// by the note. pub fn check_note_inputs( &self, note: &Note, target_account_id: AccountId, + block_ref: BlockNumber, ) -> NoteAccountCompatibility { match self { WellKnownNote::P2ID => { @@ -167,7 +161,7 @@ impl WellKnownNote { return NoteAccountCompatibility::No; } - Self::check_input_account_id(note_inputs, target_account_id) + Self::check_input_account_id(note_inputs, &[target_account_id]) }, WellKnownNote::P2IDR => { let note_inputs = note.inputs().values(); @@ -175,7 +169,21 @@ impl WellKnownNote { return NoteAccountCompatibility::No; } - Self::check_input_account_id(note_inputs, target_account_id) + let recall_hight: Result = note_inputs[2].try_into(); + match recall_hight { + // invalid input value: the block number does not fit in u32 + Err(_) => return NoteAccountCompatibility::No, + Ok(recall_hight) => { + if block_ref.as_u32() >= recall_hight { + let sender_account_id = note.metadata().sender(); + // if the sender can already reclaim the assets back, provide it as a + // valid account alongside with the target account + Self::check_input_account_id(note_inputs, &[target_account_id, sender_account_id]) + } else { + Self::check_input_account_id(note_inputs, &[target_account_id]) + } + } + } }, WellKnownNote::SWAP => { if note.inputs().values().len() != 10 { @@ -187,11 +195,12 @@ impl WellKnownNote { } } - /// Checks that the account ID, created from the first two values of the note inputs, is the - /// same as the ID of the target account. + /// Checks that the account ID, created from the first two values of the note inputs, matches at + /// least one account from the list of valid accounts. This list contains accounts which are + /// allowed to consume the note. fn check_input_account_id( note_inputs: &[Felt], - target_account_id: AccountId, + valid_accounts: &[AccountId], ) -> NoteAccountCompatibility { let account_id_felts: [Felt; 2] = note_inputs[0..2].try_into().expect( "Should be able to convert the first two note inputs to an array of two Felt elements", @@ -200,10 +209,10 @@ impl WellKnownNote { let inputs_account_id = AccountId::try_from([account_id_felts[1], account_id_felts[0]]) .expect("invalid account ID felts"); - if inputs_account_id != target_account_id { - return NoteAccountCompatibility::No; + if valid_accounts.contains(&inputs_account_id) { + NoteAccountCompatibility::Yes + } else { + NoteAccountCompatibility::No } - - NoteAccountCompatibility::Maybe } } diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index db5c0e3462..f6634dc7d0 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -23,7 +23,7 @@ mod mast_store; pub use mast_store::TransactionMastStore; mod notes_checker; -pub use notes_checker::{NoteInputsCheck, NotesChecker}; +pub use notes_checker::{NoteConsumptionChecker, NoteInputsCheck}; // TRANSACTION EXECUTOR // ================================================================================================ @@ -252,7 +252,7 @@ impl TransactionExecutor { /// - If the transaction host can not be created from the provided values. /// - If the execution of the provided program fails on the stage other than note execution. #[maybe_async] - pub(crate) fn try_notes_execution( + pub(crate) fn try_execute_notes( &self, account_id: AccountId, block_ref: BlockNumber, diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index 01bbe52b7a..c81eeacb77 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -15,20 +15,12 @@ use super::{NoteAccountExecution, TransactionExecutor, TransactionExecutorError} /// This struct performs input notes check against provided target account. /// -/// The check is performed using the [NotesChecker::check_notes_consumability] procedure. +/// The check is performed using the [NoteConsumptionChecker::check_notes_consumability] procedure. /// Essentially runs the transaction to make sure that provided input notes could be consumed by the /// account. -pub struct NotesChecker { - account_id: AccountId, - notes: Vec, -} - -impl NotesChecker { - /// Returns a new instance of the [NotesChecker]. - pub fn new(account_id: AccountId, notes: Vec) -> Self { - NotesChecker { account_id, notes } - } +pub struct NoteConsumptionChecker<'a>(pub &'a TransactionExecutor); +impl NoteConsumptionChecker<'_> { /// Checks whether the provided input notes could be consumed by the provided account. /// /// This check consists of two main steps: @@ -43,33 +35,39 @@ impl NotesChecker { #[maybe_async] pub fn check_notes_consumability( &self, - tx_executor: &TransactionExecutor, + target_account_id: AccountId, block_ref: BlockNumber, + notes: Vec, tx_args: TransactionArgs, ) -> Result { // Check input notes // ---------------------------------------------------------------------------------------- let mut successful_notes = vec![]; - for note in self.notes.iter() { + for note in notes.iter() { if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { if let WellKnownNote::SWAP = well_known_note { // if we encountered a SWAP note, then we have to execute the transaction // anyway, so we can stop checking break; - } else if let NoteAccountCompatibility::No = - well_known_note.check_note_inputs(note.note(), self.account_id) - { - // return a `Failure` with the vector of successfully checked `P2ID` and - // `P2IDR` notes if the check failed - return Ok(NoteAccountExecution::Failure { - failed_note_id: note.id(), - successful_notes, - error: None, - }); - } else { - // put the successfully checked `P2ID` or `P2IDR` note to the vector - successful_notes.push(note.id()); + } + + match well_known_note.check_note_inputs(note.note(), target_account_id, block_ref) { + NoteAccountCompatibility::No => { + // if the check failed, return a `Failure` with the vector of successfully + // checked `P2ID` and `P2IDR` notes + return Ok(NoteAccountExecution::Failure { + failed_note_id: note.id(), + successful_notes, + error: None, + }); + }, + // we need to execute the transaction if compatibility is unclear + NoteAccountCompatibility::Maybe => break, + NoteAccountCompatibility::Yes => { + // put the successfully checked `P2ID` or `P2IDR` note to the vector + successful_notes.push(note.id()); + } } } else { // if we encountered not a well known note, then we have to execute the transaction @@ -80,19 +78,14 @@ impl NotesChecker { // if all checked notes turned out to be either `P2ID` or `P2IDR` notes and all of them // passed, then we could safely return the `Success` - if successful_notes.len() == self.notes.len() { + if successful_notes.len() == notes.len() { return Ok(NoteAccountExecution::Success); } // Execute transaction // ---------------------------------------------------------------------------------------- - let note_ids = self.notes.iter().map(|note| note.id()).collect::>(); - maybe_await!(tx_executor.try_notes_execution( - self.account_id, - block_ref, - ¬e_ids, - tx_args - )) + let note_ids = notes.iter().map(|note| note.id()).collect::>(); + maybe_await!(self.0.try_execute_notes(target_account_id, block_ref, ¬e_ids, tx_args)) } } diff --git a/crates/miden-tx/src/lib.rs b/crates/miden-tx/src/lib.rs index 1706545b34..3647fc74f0 100644 --- a/crates/miden-tx/src/lib.rs +++ b/crates/miden-tx/src/lib.rs @@ -10,7 +10,7 @@ pub use miden_objects::transaction::TransactionInputs; mod executor; pub use executor::{ - DataStore, NoteInputsCheck, NotesChecker, TransactionExecutor, TransactionMastStore, + DataStore, NoteConsumptionChecker, NoteInputsCheck, TransactionExecutor, TransactionMastStore, }; pub mod host; diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index 24b874d1c3..a3cf493af2 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -53,7 +53,7 @@ use super::{ }; use crate::{ TransactionExecutorError, TransactionMastStore, - executor::{NoteAccountExecution, NotesChecker}, + executor::{NoteAccountExecution, NoteConsumptionChecker}, testing::TransactionContextBuilder, }; @@ -1089,15 +1089,20 @@ fn test_check_note_consumability() { .build(); let input_notes = tx_context.input_notes(); - let account_id = tx_context.account().id(); + let target_account_id = tx_context.account().id(); let block_ref = tx_context.tx_inputs().block_header().block_num(); - + let executor: TransactionExecutor = TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); + let notes_checker = NoteConsumptionChecker(&executor); - let notes_checker = NotesChecker::new(account_id, input_notes.clone().into_vec()); let execution_check_result = notes_checker - .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) + .check_notes_consumability( + target_account_id, + block_ref, + input_notes.clone().into_vec(), + tx_context.tx_args().clone(), + ) .unwrap(); assert_matches!(execution_check_result, NoteAccountExecution::Success); @@ -1113,10 +1118,15 @@ fn test_check_note_consumability() { let executor: TransactionExecutor = TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); + let notes_checker = NoteConsumptionChecker(&executor); - let notes_checker = NotesChecker::new(account_id, input_notes.clone().into_vec()); let execution_check_result = notes_checker - .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) + .check_notes_consumability( + account_id, + block_ref, + input_notes.clone().into_vec(), + tx_context.tx_args().clone(), + ) .unwrap(); assert_matches!(execution_check_result, NoteAccountExecution::Success); @@ -1153,10 +1163,16 @@ fn test_check_note_consumability() { let executor: TransactionExecutor = TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing(); + let notes_checker = NoteConsumptionChecker(&executor); - let notes_checker = NotesChecker::new(account_id, input_notes.clone().into_vec()); + // let notes_checker = NoteConsumptionChecker::new(account_id, input_notes.clone().into_vec()); let execution_check_result = notes_checker - .check_notes_consumability(&executor, block_ref, tx_context.tx_args().clone()) + .check_notes_consumability( + account_id, + block_ref, + input_notes.clone().into_vec(), + tx_context.tx_args().clone(), + ) .unwrap(); assert_matches!(execution_check_result, NoteAccountExecution::Failure { From 864b04a5fc2a3e8eecc20699c77da5794eb4087c Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Tue, 22 Apr 2025 15:35:50 +0300 Subject: [PATCH 14/18] chore: fix diff check, fix typo --- .../asm/kernels/transaction/lib/account.masm | 32 +++++----- crates/miden-lib/build.rs | 8 +-- .../miden-lib/src/errors/tx_kernel_errors.rs | 60 +++++++++---------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/crates/miden-lib/asm/kernels/transaction/lib/account.masm b/crates/miden-lib/asm/kernels/transaction/lib/account.masm index 2f775f7d8d..031ba18ef8 100644 --- a/crates/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/crates/miden-lib/asm/kernels/transaction/lib/account.masm @@ -10,49 +10,49 @@ use.kernel::memory # ERRORS # ================================================================================================= -# Account nonce cannot be increased by a greater than u32 value +# Account nonce cannot be increased by a greater than u32 value. const.ERR_ACCOUNT_NONCE_INCREASE_MUST_BE_U32=0x0002014a -# Account code must be updatable for it to be possible to set new code +# Account code must be updatable for it to be possible to set new code. const.ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE=0x00020142 -# Anchor block commitment must not be empty +# Anchor block commitment must not be empty. const.ERR_ACCOUNT_ANCHOR_BLOCK_COMMITMENT_MUST_NOT_BE_EMPTY=0x00020140 -# ID of the new account does not match the ID computed from the seed and anchor block commitment +# ID of the new account does not match the ID computed from the seed and anchor block commitment. const.ERR_ACCOUNT_SEED_ANCHOR_BLOCK_COMMITMENT_DIGEST_MISMATCH=0x0002014e -# Failed to write an account value item to a non-value storage slot +# Failed to write an account value item to a non-value storage slot. const.ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT=0x00020150 -# Failed to write an account map item to a non-map storage slot +# Failed to write an account map item to a non-map storage slot. const.ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT=0x0002014f -# Account procedure is not part of the account code +# Account procedure is not part of the account code. const.ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE=0x0002014c -# Provided procedure index is out of bounds +# Provided procedure index is out of bounds. const.ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS=0x0002014b -# Provided storage slot index is out of bounds +# Provided storage slot index is out of bounds. const.ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS=0x00020152 -# Storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets) +# Storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets). const.ERR_FAUCET_INVALID_STORAGE_OFFSET=0x000201c2 -# Computed account code commitment does not match recorded account code commitment +# Computed account code commitment does not match recorded account code commitment. const.ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH=0x00020141 -# Number of account procedures exceeds the maximum limit of 256 +# Number of account procedures exceeds the maximum limit of 256. const.ERR_ACCOUNT_TOO_MANY_PROCEDURES=0x00020153 -# Number of account storage slots exceeds the maximum limit of 255 +# Number of account storage slots exceeds the maximum limit of 255. const.ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS=0x00020154 -# Computed account storage commitment does not match recorded account storage commitment +# Computed account storage commitment does not match recorded account storage commitment. const.ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH=0x00020151 -# Storage offset is invalid for 0 storage size (should be 0) +# Storage offset is invalid for 0 storage size (should be 0). const.ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE=0x00020147 # ID of the provided foreign account equals zero. @@ -73,7 +73,7 @@ const.ERR_ACCOUNT_ID_EPOCH_MUST_BE_LESS_THAN_U16_MAX=0x00020143 # Unknown account storage mode in account ID. const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE=0x00020145 -# Failed to read an account map item from a non-map storage slot +# Failed to read an account map item from a non-map storage slot. const.ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT=0x0002014d # CONSTANTS diff --git a/crates/miden-lib/build.rs b/crates/miden-lib/build.rs index 7aec2f3434..f2919b7aee 100644 --- a/crates/miden-lib/build.rs +++ b/crates/miden-lib/build.rs @@ -54,7 +54,7 @@ const TX_KERNEL_ERROR_CATEGORIES: [TxKernelErrorCategory; 11] = [ TxKernelErrorCategory::ForeignAccount, TxKernelErrorCategory::Faucet, TxKernelErrorCategory::FungibleAsset, - TxKernelErrorCategory::NonFugibleAsset, + TxKernelErrorCategory::NonFungibleAsset, TxKernelErrorCategory::Vault, ]; @@ -839,7 +839,7 @@ enum TxKernelErrorCategory { ForeignAccount, Faucet, FungibleAsset, - NonFugibleAsset, + NonFungibleAsset, Vault, } @@ -855,7 +855,7 @@ impl TxKernelErrorCategory { TxKernelErrorCategory::ForeignAccount => 0x2_0180..0x2_01c0, TxKernelErrorCategory::Faucet => 0x2_01c0..0x2_0200, TxKernelErrorCategory::FungibleAsset => 0x2_0200..0x2_0240, - TxKernelErrorCategory::NonFugibleAsset => 0x2_0240..0x2_0280, + TxKernelErrorCategory::NonFungibleAsset => 0x2_0240..0x2_0280, TxKernelErrorCategory::Vault => 0x2_0280..0x2_02c0, } } @@ -871,7 +871,7 @@ impl TxKernelErrorCategory { TxKernelErrorCategory::ForeignAccount => "FOREIGN_ACCOUNT", TxKernelErrorCategory::Faucet => "FAUCET", TxKernelErrorCategory::FungibleAsset => "FUNGIBLE_ASSET", - TxKernelErrorCategory::NonFugibleAsset => "NON_FUNGIBLE_ASSET", + TxKernelErrorCategory::NonFungibleAsset => "NON_FUNGIBLE_ASSET", TxKernelErrorCategory::Vault => "VAULT", } } diff --git a/crates/miden-lib/src/errors/tx_kernel_errors.rs b/crates/miden-lib/src/errors/tx_kernel_errors.rs index 06843da29f..b01f4db019 100644 --- a/crates/miden-lib/src/errors/tx_kernel_errors.rs +++ b/crates/miden-lib/src/errors/tx_kernel_errors.rs @@ -95,11 +95,11 @@ pub const ERR_NOTE_TAG_MUST_BE_U32: u32 = 0x20109; /// Network execution mode with a specific target can only target network accounts. pub const ERR_NOTE_NETWORK_EXECUTION_DOES_NOT_TARGET_NETWORK_ACCOUNT: u32 = 0x2010a; -/// Anchor block commitment must not be empty +/// Anchor block commitment must not be empty. pub const ERR_ACCOUNT_ANCHOR_BLOCK_COMMITMENT_MUST_NOT_BE_EMPTY: u32 = 0x20140; -/// Computed account code commitment does not match recorded account code commitment +/// Computed account code commitment does not match recorded account code commitment. pub const ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH: u32 = 0x20141; -/// Account code must be updatable for it to be possible to set new code +/// Account code must be updatable for it to be possible to set new code. pub const ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE: u32 = 0x20142; /// Epoch must be less than u16::MAX (0xffff). pub const ERR_ACCOUNT_ID_EPOCH_MUST_BE_LESS_THAN_U16_MAX: u32 = 0x20143; @@ -109,33 +109,33 @@ pub const ERR_ACCOUNT_ID_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO: u32 = 0x20144; pub const ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE: u32 = 0x20145; /// Unknown version in account ID. pub const ERR_ACCOUNT_ID_UNKNOWN_VERSION: u32 = 0x20146; -/// Storage offset is invalid for 0 storage size (should be 0) +/// Storage offset is invalid for 0 storage size (should be 0). pub const ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE: u32 = 0x20147; /// The current account is not native pub const ERR_ACCOUNT_IS_NOT_NATIVE: u32 = 0x20148; /// Account nonce did not increase after a state changing transaction pub const ERR_ACCOUNT_NONCE_DID_NOT_INCREASE_AFTER_STATE_CHANGE: u32 = 0x20149; -/// Account nonce cannot be increased by a greater than u32 value +/// Account nonce cannot be increased by a greater than u32 value. pub const ERR_ACCOUNT_NONCE_INCREASE_MUST_BE_U32: u32 = 0x2014a; -/// Provided procedure index is out of bounds +/// Provided procedure index is out of bounds. pub const ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS: u32 = 0x2014b; -/// Account procedure is not part of the account code +/// Account procedure is not part of the account code. pub const ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE: u32 = 0x2014c; -/// Failed to read an account map item from a non-map storage slot +/// Failed to read an account map item from a non-map storage slot. pub const ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT: u32 = 0x2014d; -/// ID of the new account does not match the ID computed from the seed and anchor block commitment +/// ID of the new account does not match the ID computed from the seed and anchor block commitment. pub const ERR_ACCOUNT_SEED_ANCHOR_BLOCK_COMMITMENT_DIGEST_MISMATCH: u32 = 0x2014e; -/// Failed to write an account map item to a non-map storage slot +/// Failed to write an account map item to a non-map storage slot. pub const ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT: u32 = 0x2014f; -/// Failed to write an account value item to a non-value storage slot +/// Failed to write an account value item to a non-value storage slot. pub const ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT: u32 = 0x20150; -/// Computed account storage commitment does not match recorded account storage commitment +/// Computed account storage commitment does not match recorded account storage commitment. pub const ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH: u32 = 0x20151; /// Provided storage slot index is out of bounds. pub const ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS: u32 = 0x20152; -/// Number of account procedures exceeds the maximum limit of 256 +/// Number of account procedures exceeds the maximum limit of 256. pub const ERR_ACCOUNT_TOO_MANY_PROCEDURES: u32 = 0x20153; -/// Number of account storage slots exceeds the maximum limit of 255 +/// Number of account storage slots exceeds the maximum limit of 255. pub const ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS: u32 = 0x20154; /// Depth of the nested FPI calls exceeded 64 pub const ERR_ACCOUNT_STACK_OVERFLOW: u32 = 0x20155; @@ -157,7 +157,7 @@ pub const ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED: u32 = 0x20183; pub const ERR_FAUCET_BURN_CANNOT_EXCEED_EXISTING_TOTAL_SUPPLY: u32 = 0x201c0; /// The burn_non_fungible_asset procedure can only be called on a non-fungible faucet pub const ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET: u32 = 0x201c1; -/// Storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets) +/// Storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets). pub const ERR_FAUCET_INVALID_STORAGE_OFFSET: u32 = 0x201c2; /// The faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet pub const ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET: u32 = 0x201c3; @@ -254,27 +254,27 @@ pub const TX_KERNEL_ERRORS: [(u32, &str); 90] = [ (ERR_NOTE_TAG_MUST_BE_U32, "The note's tag must fit into a u32 so the 32 most significant bits must be zero."), (ERR_NOTE_NETWORK_EXECUTION_DOES_NOT_TARGET_NETWORK_ACCOUNT, "Network execution mode with a specific target can only target network accounts."), - (ERR_ACCOUNT_ANCHOR_BLOCK_COMMITMENT_MUST_NOT_BE_EMPTY, "Anchor block commitment must not be empty"), - (ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH, "Computed account code commitment does not match recorded account code commitment"), - (ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE, "Account code must be updatable for it to be possible to set new code"), + (ERR_ACCOUNT_ANCHOR_BLOCK_COMMITMENT_MUST_NOT_BE_EMPTY, "Anchor block commitment must not be empty."), + (ERR_ACCOUNT_CODE_COMMITMENT_MISMATCH, "Computed account code commitment does not match recorded account code commitment."), + (ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE, "Account code must be updatable for it to be possible to set new code."), (ERR_ACCOUNT_ID_EPOCH_MUST_BE_LESS_THAN_U16_MAX, "Epoch must be less than u16::MAX (0xffff)."), (ERR_ACCOUNT_ID_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO, "Least significant byte of the account ID suffix must be zero."), (ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE, "Unknown account storage mode in account ID."), (ERR_ACCOUNT_ID_UNKNOWN_VERSION, "Unknown version in account ID."), - (ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE, "Storage offset is invalid for 0 storage size (should be 0)"), + (ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE, "Storage offset is invalid for 0 storage size (should be 0)."), (ERR_ACCOUNT_IS_NOT_NATIVE, "The current account is not native"), (ERR_ACCOUNT_NONCE_DID_NOT_INCREASE_AFTER_STATE_CHANGE, "Account nonce did not increase after a state changing transaction"), - (ERR_ACCOUNT_NONCE_INCREASE_MUST_BE_U32, "Account nonce cannot be increased by a greater than u32 value"), - (ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS, "Provided procedure index is out of bounds"), - (ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE, "Account procedure is not part of the account code"), - (ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT, "Failed to read an account map item from a non-map storage slot"), - (ERR_ACCOUNT_SEED_ANCHOR_BLOCK_COMMITMENT_DIGEST_MISMATCH, "ID of the new account does not match the ID computed from the seed and anchor block commitment"), - (ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT, "Failed to write an account map item to a non-map storage slot"), - (ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT, "Failed to write an account value item to a non-value storage slot"), - (ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH, "Computed account storage commitment does not match recorded account storage commitment"), + (ERR_ACCOUNT_NONCE_INCREASE_MUST_BE_U32, "Account nonce cannot be increased by a greater than u32 value."), + (ERR_ACCOUNT_PROC_INDEX_OUT_OF_BOUNDS, "Provided procedure index is out of bounds."), + (ERR_ACCOUNT_PROC_NOT_PART_OF_ACCOUNT_CODE, "Account procedure is not part of the account code."), + (ERR_ACCOUNT_READING_MAP_VALUE_FROM_NON_MAP_SLOT, "Failed to read an account map item from a non-map storage slot."), + (ERR_ACCOUNT_SEED_ANCHOR_BLOCK_COMMITMENT_DIGEST_MISMATCH, "ID of the new account does not match the ID computed from the seed and anchor block commitment."), + (ERR_ACCOUNT_SETTING_MAP_ITEM_ON_NON_MAP_SLOT, "Failed to write an account map item to a non-map storage slot."), + (ERR_ACCOUNT_SETTING_VALUE_ITEM_ON_NON_VALUE_SLOT, "Failed to write an account value item to a non-value storage slot."), + (ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH, "Computed account storage commitment does not match recorded account storage commitment."), (ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS, "Provided storage slot index is out of bounds."), - (ERR_ACCOUNT_TOO_MANY_PROCEDURES, "Number of account procedures exceeds the maximum limit of 256"), - (ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS, "Number of account storage slots exceeds the maximum limit of 255"), + (ERR_ACCOUNT_TOO_MANY_PROCEDURES, "Number of account procedures exceeds the maximum limit of 256."), + (ERR_ACCOUNT_TOO_MANY_STORAGE_SLOTS, "Number of account storage slots exceeds the maximum limit of 255."), (ERR_ACCOUNT_STACK_OVERFLOW, "Depth of the nested FPI calls exceeded 64"), (ERR_ACCOUNT_STACK_UNDERFLOW, "Failed to end foreign context because the current account is the native account"), (ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT, "The account ID must have storage mode public if the network flag is set."), @@ -286,7 +286,7 @@ pub const TX_KERNEL_ERRORS: [(u32, &str); 90] = [ (ERR_FAUCET_BURN_CANNOT_EXCEED_EXISTING_TOTAL_SUPPLY, "Asset amount to burn can not exceed the existing total supply"), (ERR_FAUCET_BURN_NON_FUNGIBLE_ASSET_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET, "The burn_non_fungible_asset procedure can only be called on a non-fungible faucet"), - (ERR_FAUCET_INVALID_STORAGE_OFFSET, "Storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets)"), + (ERR_FAUCET_INVALID_STORAGE_OFFSET, "Storage offset is invalid for a faucet account (0 is prohibited as it is the reserved data slot for faucets)."), (ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET, "The faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet"), (ERR_FAUCET_NEW_TOTAL_SUPPLY_WOULD_EXCEED_MAX_ASSET_AMOUNT, "Asset mint operation would cause the new total supply to exceed the maximum allowed asset amount"), (ERR_FAUCET_NON_FUNGIBLE_ASSET_ALREADY_ISSUED, "Failed to mint new non-fungible asset because it was already issued"), From 6c275ed1252d2e6d9d69042a4575419d4c1c6668 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Sat, 26 Apr 2025 21:36:07 +0300 Subject: [PATCH 15/18] refactor: update note checking logic, small improvements --- crates/miden-lib/src/note/well_known_note.rs | 45 ++++++++------- crates/miden-tx/src/executor/mod.rs | 57 +++++++++---------- crates/miden-tx/src/executor/notes_checker.rs | 19 +++++-- crates/miden-tx/src/tests/mod.rs | 6 +- 4 files changed, 67 insertions(+), 60 deletions(-) diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index 9b8b606146..d08f891b78 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -152,8 +152,10 @@ impl WellKnownNote { /// /// It performs: /// - for all notes: a check that note inputs have correct number of values. - /// - for `P2ID` and `P2IDR` notes: assertion that the account ID provided by the note inputs is - /// equal to the target account ID. + /// - for `P2ID` note: assertion that the account ID provided by the note inputs is equal to the + /// target account ID. + /// - for `P2IDR` note: assertion that the account ID provided by the note inputs is equal to + /// the target or sender account IDs. pub fn check_note_inputs( &self, note: &Note, @@ -175,23 +177,21 @@ impl WellKnownNote { return NoteAccountCompatibility::No; } - let recall_hight: Result = note_inputs[2].try_into(); - match recall_hight { - // invalid input value: the block number does not fit in u32 - Err(_) => NoteAccountCompatibility::No, - Ok(recall_hight) => { - if block_ref.as_u32() >= recall_hight { - let sender_account_id = note.metadata().sender(); - // if the sender can already reclaim the assets back, provide it as a - // valid account alongside with the target account - Self::check_input_account_id( - note_inputs, - &[target_account_id, sender_account_id], - ) - } else { - Self::check_input_account_id(note_inputs, &[target_account_id]) - } - }, + let recall_height: Result = note_inputs[2].try_into(); + let Ok(recall_height) = recall_height else { + return NoteAccountCompatibility::No; + }; + + if block_ref.as_u32() >= recall_height { + let sender_account_id = note.metadata().sender(); + // if the sender can already reclaim the assets back, provide it as a + // valid account alongside with the target account + Self::check_input_account_id( + note_inputs, + &[target_account_id, sender_account_id], + ) + } else { + Self::check_input_account_id(note_inputs, &[target_account_id]) } }, WellKnownNote::SWAP => { @@ -215,8 +215,11 @@ impl WellKnownNote { "Should be able to convert the first two note inputs to an array of two Felt elements", ); - let inputs_account_id = AccountId::try_from([account_id_felts[1], account_id_felts[0]]) - .expect("invalid account ID felts"); + let Ok(inputs_account_id) = AccountId::try_from([account_id_felts[1], account_id_felts[0]]) + else { + // return No if the account ID felts are invalid + return NoteAccountCompatibility::No; + }; if valid_accounts.contains(&inputs_account_id) { NoteAccountCompatibility::Yes diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index ffa736ca94..c89b421ec4 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -111,21 +111,7 @@ impl TransactionExecutor { notes: InputNotes, tx_args: TransactionArgs, ) -> Result { - // Validate that notes were not created after the reference, and build the set of required - // block numbers - let mut ref_blocks: BTreeSet = BTreeSet::new(); - for note in ¬es { - if let Some(location) = note.location() { - if location.block_num() > block_ref { - return Err(TransactionExecutorError::NoteBlockPastReferenceBlock( - note.id(), - block_ref, - )); - } - ref_blocks.insert(location.block_num()); - } - } - + let mut ref_blocks = validate_input_notes(¬es, block_ref)?; ref_blocks.insert(block_ref); let (account, seed, ref_block, mmr) = @@ -246,21 +232,7 @@ impl TransactionExecutor { notes: InputNotes, tx_args: TransactionArgs, ) -> Result { - // Validate that notes were not created after the reference, and build the set of required - // block numbers - let mut ref_blocks: BTreeSet = BTreeSet::new(); - for note in ¬es { - if let Some(location) = note.location() { - if location.block_num() > block_ref { - return Err(TransactionExecutorError::NoteBlockPastReferenceBlock( - note.id(), - block_ref, - )); - } - ref_blocks.insert(location.block_num()); - } - } - + let mut ref_blocks = validate_input_notes(¬es, block_ref)?; ref_blocks.insert(block_ref); let (account, seed, ref_block, mmr) = @@ -405,6 +377,31 @@ fn validate_account_inputs( Ok(()) } +/// Validates that input notes were not created after the reference block. +/// +/// Returns the set of block numbers required to execute the provided notes. +fn validate_input_notes( + notes: &InputNotes, + block_ref: BlockNumber, +) -> Result, TransactionExecutorError> { + // Validate that notes were not created after the reference, and build the set of required + // block numbers + let mut ref_blocks: BTreeSet = BTreeSet::new(); + for note in notes.iter() { + if let Some(location) = note.location() { + if location.block_num() > block_ref { + return Err(TransactionExecutorError::NoteBlockPastReferenceBlock( + note.id(), + block_ref, + )); + } + ref_blocks.insert(location.block_num()); + } + } + + Ok(ref_blocks) +} + // HELPER ENUM // ================================================================================================ diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index 371268268e..0f5cb66926 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -16,9 +16,14 @@ use super::{NoteAccountExecution, TransactionExecutor, TransactionExecutorError} /// The check is performed using the [NoteConsumptionChecker::check_notes_consumability] procedure. /// Essentially runs the transaction to make sure that provided input notes could be consumed by the /// account. -pub struct NoteConsumptionChecker<'a>(pub &'a TransactionExecutor); +pub struct NoteConsumptionChecker<'a>(&'a TransactionExecutor); + +impl<'a> NoteConsumptionChecker<'a> { + /// Creates a new [`NoteConsumptionChecker`] instance with the given transaction executor. + pub fn new(tx_executor: &'a TransactionExecutor) -> Self { + NoteConsumptionChecker(tx_executor) + } -impl NoteConsumptionChecker<'_> { /// Checks whether the provided input notes could be consumed by the provided account. /// /// This check consists of two main steps: @@ -46,8 +51,9 @@ impl NoteConsumptionChecker<'_> { if let Some(well_known_note) = WellKnownNote::from_note(note.note()) { if let WellKnownNote::SWAP = well_known_note { // if we encountered a SWAP note, then we have to execute the transaction - // anyway, so we can stop checking - break; + // anyway, but we should continue iterating to make sure that there are no + // P2ID(R) notes which return a `No` + continue; } match well_known_note.check_note_inputs(note.note(), target_account_id, block_ref) { @@ -69,8 +75,9 @@ impl NoteConsumptionChecker<'_> { } } else { // if we encountered not a well known note, then we have to execute the transaction - // anyway, so we can stop checking - break; + // anyway, but we should continue iterating to make sure that there are no + // P2ID(R) notes which return a `No` + continue; } } diff --git a/crates/miden-tx/src/tests/mod.rs b/crates/miden-tx/src/tests/mod.rs index ca69cfadf5..9cd86f9831 100644 --- a/crates/miden-tx/src/tests/mod.rs +++ b/crates/miden-tx/src/tests/mod.rs @@ -1004,7 +1004,7 @@ fn test_check_note_consumability() { let executor: TransactionExecutor = TransactionExecutor::new(Arc::new(tx_context), None).with_tracing(); - let notes_checker = NoteConsumptionChecker(&executor); + let notes_checker = NoteConsumptionChecker::new(&executor); let execution_check_result = notes_checker .check_notes_consumability(target_account_id, block_ref, input_notes, tx_args) @@ -1024,7 +1024,7 @@ fn test_check_note_consumability() { let executor: TransactionExecutor = TransactionExecutor::new(Arc::new(tx_context), None).with_tracing(); - let notes_checker = NoteConsumptionChecker(&executor); + let notes_checker = NoteConsumptionChecker::new(&executor); let execution_check_result = notes_checker .check_notes_consumability(account_id, block_ref, input_notes, tx_args) @@ -1065,7 +1065,7 @@ fn test_check_note_consumability() { let executor: TransactionExecutor = TransactionExecutor::new(Arc::new(tx_context), None).with_tracing(); - let notes_checker = NoteConsumptionChecker(&executor); + let notes_checker = NoteConsumptionChecker::new(&executor); // let notes_checker = NoteConsumptionChecker::new(account_id, input_notes.clone().into_vec()); let execution_check_result = notes_checker From 4416a62b190e0de45abcfe4996118735eb5ee4b5 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Sat, 26 Apr 2025 21:51:11 +0300 Subject: [PATCH 16/18] chore: fix diff check --- crates/miden-lib/asm/shared/util/account_id.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-lib/asm/shared/util/account_id.masm b/crates/miden-lib/asm/shared/util/account_id.masm index 23ffc4f38b..5481f8d5c6 100644 --- a/crates/miden-lib/asm/shared/util/account_id.masm +++ b/crates/miden-lib/asm/shared/util/account_id.masm @@ -13,7 +13,7 @@ const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE=0x00020145 # Least significant byte of the account ID suffix must be zero. const.ERR_ACCOUNT_ID_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO=0x00020144 -# Provided storage slot index is out of bounds +# Provided storage slot index is out of bounds. const.ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS=0x00020152 # The account ID must have storage mode public if the network flag is set. From fd6c1c1c689da92ea8d6faff3a739b5b1643d2f4 Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Sun, 4 May 2025 00:06:20 +0300 Subject: [PATCH 17/18] refactor: update P2IDR check logic, rework check_input_account_id function --- crates/miden-lib/src/note/well_known_note.rs | 81 +++++++++++-------- crates/miden-tx/src/executor/notes_checker.rs | 6 +- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index d08f891b78..0f0030d4de 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -155,7 +155,9 @@ impl WellKnownNote { /// - for `P2ID` note: assertion that the account ID provided by the note inputs is equal to the /// target account ID. /// - for `P2IDR` note: assertion that the account ID provided by the note inputs is equal to - /// the target or sender account IDs. + /// the target account ID (which means that the note is going to be consumed by the target + /// account) or that the target account ID is equal to the sender account ID (which means that + /// the note is going to be consumed by the sender account) pub fn check_note_inputs( &self, note: &Note, @@ -169,7 +171,17 @@ impl WellKnownNote { return NoteAccountCompatibility::No; } - Self::check_input_account_id(note_inputs, &[target_account_id]) + // Return `No` if the note input values used to construct the account ID are invalid + let Some(input_account_id) = try_read_account_id_from_inputs(note_inputs) else { + return NoteAccountCompatibility::No; + }; + + // check that the account ID in the note inputs equal to the target account ID + if input_account_id == target_account_id { + NoteAccountCompatibility::Yes + } else { + NoteAccountCompatibility::No + } }, WellKnownNote::P2IDR => { let note_inputs = note.inputs().values(); @@ -178,20 +190,35 @@ impl WellKnownNote { } let recall_height: Result = note_inputs[2].try_into(); + // Return `No` if the note input value which represents the recall height is invalid let Ok(recall_height) = recall_height else { return NoteAccountCompatibility::No; }; + // Return `No` if the note input values used to construct the account ID are invalid + let Some(input_account_id) = try_read_account_id_from_inputs(note_inputs) else { + return NoteAccountCompatibility::No; + }; + if block_ref.as_u32() >= recall_height { let sender_account_id = note.metadata().sender(); - // if the sender can already reclaim the assets back, provide it as a - // valid account alongside with the target account - Self::check_input_account_id( - note_inputs, - &[target_account_id, sender_account_id], - ) + // if the sender can already reclaim the assets back, then: + // - target account ID could be equal to the inputs account ID if the note is + // going to be consumed by the target account + // - target account ID could be equal to the sender account ID if the note is + // going to be consumed by the sender account + if [input_account_id, sender_account_id].contains(&target_account_id) { + NoteAccountCompatibility::Yes + } else { + NoteAccountCompatibility::No + } } else { - Self::check_input_account_id(note_inputs, &[target_account_id]) + // in this case note could be consumed only by the target account + if input_account_id == target_account_id { + NoteAccountCompatibility::Yes + } else { + NoteAccountCompatibility::No + } } }, WellKnownNote::SWAP => { @@ -203,28 +230,18 @@ impl WellKnownNote { }, } } +} - /// Checks that the account ID, created from the first two values of the note inputs, matches at - /// least one account from the list of valid accounts. This list contains accounts which are - /// allowed to consume the note. - fn check_input_account_id( - note_inputs: &[Felt], - valid_accounts: &[AccountId], - ) -> NoteAccountCompatibility { - let account_id_felts: [Felt; 2] = note_inputs[0..2].try_into().expect( - "Should be able to convert the first two note inputs to an array of two Felt elements", - ); - - let Ok(inputs_account_id) = AccountId::try_from([account_id_felts[1], account_id_felts[0]]) - else { - // return No if the account ID felts are invalid - return NoteAccountCompatibility::No; - }; - - if valid_accounts.contains(&inputs_account_id) { - NoteAccountCompatibility::Yes - } else { - NoteAccountCompatibility::No - } - } +// HELPER FUNCTIONS +// ================================================================================================ + +/// Reads the account ID from the note inputs. +/// +/// Returns None if the note input values used to construct the account ID are invalid. +fn try_read_account_id_from_inputs(note_inputs: &[Felt]) -> Option { + let account_id_felts: [Felt; 2] = note_inputs[0..2].try_into().expect( + "Should be able to convert the first two note inputs to an array of two Felt elements", + ); + + AccountId::try_from([account_id_felts[1], account_id_felts[0]]).ok() } diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index 0f5cb66926..9e214739be 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -66,8 +66,10 @@ impl<'a> NoteConsumptionChecker<'a> { error: None, }); }, - // we need to execute the transaction if compatibility is unclear - NoteAccountCompatibility::Maybe => break, + // this branch is unreachable, since we are handling the SWAP note separately, + // but as an extra precaution continue iterating over the notes and run the + // transaction to make sure the note which returned "Maybe" could be consumed + NoteAccountCompatibility::Maybe => continue, NoteAccountCompatibility::Yes => { // put the successfully checked `P2ID` or `P2IDR` note to the vector successful_notes.push(note.id()); From e0963f9f331b66c9223da8a889d73350527f385f Mon Sep 17 00:00:00 2001 From: Andrey Khmuro Date: Mon, 5 May 2025 13:39:50 +0300 Subject: [PATCH 18/18] refactor: add constants for note input nums, update doc comments --- crates/miden-lib/src/note/well_known_note.rs | 35 +++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/crates/miden-lib/src/note/well_known_note.rs b/crates/miden-lib/src/note/well_known_note.rs index 0f0030d4de..1ad1748e4b 100644 --- a/crates/miden-lib/src/note/well_known_note.rs +++ b/crates/miden-lib/src/note/well_known_note.rs @@ -78,6 +78,21 @@ pub enum WellKnownNote { } impl WellKnownNote { + // CONSTANTS + // -------------------------------------------------------------------------------------------- + + /// Expected number of inputs of the P2ID note. + const P2ID_NUM_INPUTS: usize = 2; + + /// Expected number of inputs of the P2IDR note. + const P2IDR_NUM_INPUTS: usize = 3; + + /// Expected number of inputs of the SWAP note. + const SWAP_NUM_INPUTS: usize = 10; + + // CONSTRUCTOR + // -------------------------------------------------------------------------------------------- + /// Returns a [WellKnownNote] instance based on the note script of the provided [Note]. Returns /// `None` if the provided note is not a basic well-known note. pub fn from_note(note: &Note) -> Option { @@ -96,6 +111,18 @@ impl WellKnownNote { None } + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the expected inputs number of the current note. + pub fn num_expected_inputs(&self) -> usize { + match self { + Self::P2ID => Self::P2ID_NUM_INPUTS, + Self::P2IDR => Self::P2IDR_NUM_INPUTS, + Self::SWAP => Self::SWAP_NUM_INPUTS, + } + } + /// Returns the note script of the current [WellKnownNote] instance. pub fn script(&self) -> NoteScript { match self { @@ -167,7 +194,7 @@ impl WellKnownNote { match self { WellKnownNote::P2ID => { let note_inputs = note.inputs().values(); - if note_inputs.len() != 2 { + if note_inputs.len() != self.num_expected_inputs() { return NoteAccountCompatibility::No; } @@ -185,7 +212,7 @@ impl WellKnownNote { }, WellKnownNote::P2IDR => { let note_inputs = note.inputs().values(); - if note_inputs.len() != 3 { + if note_inputs.len() != self.num_expected_inputs() { return NoteAccountCompatibility::No; } @@ -222,7 +249,7 @@ impl WellKnownNote { } }, WellKnownNote::SWAP => { - if note.inputs().values().len() != 10 { + if note.inputs().values().len() != self.num_expected_inputs() { return NoteAccountCompatibility::No; } @@ -235,7 +262,7 @@ impl WellKnownNote { // HELPER FUNCTIONS // ================================================================================================ -/// Reads the account ID from the note inputs. +/// Reads the account ID from the first two note input values. /// /// Returns None if the note input values used to construct the account ID are invalid. fn try_read_account_id_from_inputs(note_inputs: &[Felt]) -> Option {