miden_tx/executor/notes_checker.rs
1use alloc::sync::Arc;
2
3use miden_lib::{
4 account::interface::NoteAccountCompatibility, note::well_known_note::WellKnownNote,
5};
6use miden_objects::{
7 account::AccountId,
8 assembly::SourceManager,
9 block::BlockNumber,
10 note::NoteId,
11 transaction::{InputNote, InputNotes, TransactionArgs},
12};
13use winter_maybe_async::{maybe_async, maybe_await};
14
15use super::{NoteAccountExecution, TransactionExecutor, TransactionExecutorError};
16
17/// This struct performs input notes check against provided target account.
18///
19/// The check is performed using the [NoteConsumptionChecker::check_notes_consumability] procedure.
20/// Essentially runs the transaction to make sure that provided input notes could be consumed by the
21/// account.
22pub struct NoteConsumptionChecker<'a>(&'a TransactionExecutor);
23
24impl<'a> NoteConsumptionChecker<'a> {
25 /// Creates a new [`NoteConsumptionChecker`] instance with the given transaction executor.
26 pub fn new(tx_executor: &'a TransactionExecutor) -> Self {
27 NoteConsumptionChecker(tx_executor)
28 }
29
30 /// Checks whether the provided input notes could be consumed by the provided account.
31 ///
32 /// This check consists of two main steps:
33 /// - Statically check the notes: if all notes are either `P2ID` or `P2IDR` notes with correct
34 /// inputs, return `NoteAccountExecution::Success`.
35 /// - Execute the transaction:
36 /// - Returns `NoteAccountExecution::Success` if the execution was successful.
37 /// - Returns `NoteAccountExecution::Failure` if some note returned an error. The fields
38 /// associated with `Failure` variant contains the ID of the failed note, a vector of IDs of
39 /// the notes, which were successfully executed, and the [TransactionExecutorError] if the
40 /// check failed durning the execution stage.
41 #[maybe_async]
42 pub fn check_notes_consumability(
43 &self,
44 target_account_id: AccountId,
45 block_ref: BlockNumber,
46 input_notes: InputNotes<InputNote>,
47 tx_args: TransactionArgs,
48 source_manager: Arc<dyn SourceManager>,
49 ) -> Result<NoteAccountExecution, TransactionExecutorError> {
50 // Check input notes
51 // ----------------------------------------------------------------------------------------
52
53 let mut successful_notes = vec![];
54 for note in input_notes.iter() {
55 if let Some(well_known_note) = WellKnownNote::from_note(note.note()) {
56 if let WellKnownNote::SWAP = well_known_note {
57 // if we encountered a SWAP note, then we have to execute the transaction
58 // anyway, but we should continue iterating to make sure that there are no
59 // P2ID(R) notes which return a `No`
60 continue;
61 }
62
63 match well_known_note.check_note_inputs(note.note(), target_account_id, block_ref) {
64 NoteAccountCompatibility::No => {
65 // if the check failed, return a `Failure` with the vector of successfully
66 // checked `P2ID` and `P2IDR` notes
67 return Ok(NoteAccountExecution::Failure {
68 failed_note_id: note.id(),
69 successful_notes,
70 error: None,
71 });
72 },
73 // this branch is unreachable, since we are handling the SWAP note separately,
74 // but as an extra precaution continue iterating over the notes and run the
75 // transaction to make sure the note which returned "Maybe" could be consumed
76 NoteAccountCompatibility::Maybe => continue,
77 NoteAccountCompatibility::Yes => {
78 // put the successfully checked `P2ID` or `P2IDR` note to the vector
79 successful_notes.push(note.id());
80 },
81 }
82 } else {
83 // if we encountered not a well known note, then we have to execute the transaction
84 // anyway, but we should continue iterating to make sure that there are no
85 // P2ID(R) notes which return a `No`
86 continue;
87 }
88 }
89
90 // if all checked notes turned out to be either `P2ID` or `P2IDR` notes and all of them
91 // passed, then we could safely return the `Success`
92 if successful_notes.len() == input_notes.num_notes() {
93 return Ok(NoteAccountExecution::Success);
94 }
95
96 // Execute transaction
97 // ----------------------------------------------------------------------------------------
98 maybe_await!(self.0.try_execute_notes(
99 target_account_id,
100 block_ref,
101 input_notes,
102 tx_args,
103 source_manager
104 ))
105 }
106}
107
108/// Helper enum for getting a result of the well known note inputs check.
109#[derive(Debug, PartialEq)]
110pub enum NoteInputsCheck {
111 Maybe,
112 No { failed_note_id: NoteId },
113}