1use alloc::vec::Vec;
4
5use miden_protocol::account::AccountId;
6use miden_protocol::asset::Asset;
7use miden_protocol::note::NoteType;
8use miden_protocol::transaction::ExecutedTransaction;
9
10#[doc(hidden)]
12#[derive(Default, Debug, Clone)]
13pub struct OutputNoteSpec {
14 pub note_type: Option<NoteType>,
15 pub sender: Option<AccountId>,
16 pub assets: Option<Vec<Asset>>,
17}
18
19#[doc(hidden)]
21pub fn check_output_note_created(tx: &ExecutedTransaction, spec: &OutputNoteSpec) -> bool {
22 tx.output_notes().iter().any(|note| {
23 if let Some(expected) = spec.note_type
24 && note.metadata().note_type() != expected
25 {
26 return false;
27 }
28 if let Some(expected) = spec.sender
29 && note.metadata().sender() != expected
30 {
31 return false;
32 }
33 if let Some(expected) = spec.assets.as_ref() {
34 let actual = note.assets();
35 if actual.num_assets() != expected.len() {
36 return false;
37 }
38 let mut consumed = vec![false; expected.len()];
40 let matched = expected.iter().all(|exp| {
41 let slot = actual.iter().enumerate().find(|(i, a)| !consumed[*i] && *a == exp);
42 if let Some((i, _)) = slot {
43 consumed[i] = true;
44 true
45 } else {
46 false
47 }
48 });
49 if !matched {
50 return false;
51 }
52 }
53 true
54 })
55}
56
57#[macro_export]
72macro_rules! assert_note_created {
73 ($tx:expr $(, $key:ident : $val:expr)* $(,)?) => {{
74 #[allow(unused_mut)]
75 let mut spec = $crate::asserts::OutputNoteSpec::default();
76 $(
77 $crate::__assert_note_created_field!(spec, $key, $val);
78 )*
79 let tx: &::miden_protocol::transaction::ExecutedTransaction = &$tx;
80 assert!(
81 $crate::asserts::check_output_note_created(tx, &spec),
82 "no output note matches spec: {:?}\n tx produced {} output note(s)",
83 spec,
84 tx.output_notes().num_notes(),
85 );
86 }};
87}
88
89#[doc(hidden)]
90#[macro_export]
91macro_rules! __assert_note_created_field {
92 ($spec:ident,note_type, $val:expr) => {
93 $spec.note_type = ::core::option::Option::Some($val);
94 };
95 ($spec:ident,sender, $val:expr) => {
96 $spec.sender = ::core::option::Option::Some($val);
97 };
98 ($spec:ident,assets, $val:expr) => {
99 $spec.assets = ::core::option::Option::Some(
100 ::core::iter::IntoIterator::into_iter($val)
101 .map(::core::convert::Into::into)
102 .collect::<::alloc::vec::Vec<::miden_protocol::asset::Asset>>(),
103 );
104 };
105 ($spec:ident, $key:ident, $val:expr) => {
106 ::core::compile_error!(concat!(
107 "unknown field in assert_note_created!: `",
108 stringify!($key),
109 "`. Supported fields: note_type, sender, assets",
110 ));
111 };
112}