miden_testing/
utils.rs

1use alloc::{string::String, vec::Vec};
2
3use miden_lib::transaction::{TransactionKernel, memory};
4use miden_objects::{
5    account::AccountId,
6    asset::Asset,
7    note::Note,
8    testing::{note::NoteBuilder, storage::prepare_assets},
9};
10use miden_tx::utils::word_to_masm_push_string;
11use rand::{SeedableRng, rngs::SmallRng};
12use vm_processor::Felt;
13
14// TEST HELPERS
15// ================================================================================================
16
17pub fn input_note_data_ptr(note_idx: u32) -> memory::MemoryAddress {
18    memory::INPUT_NOTE_DATA_SECTION_OFFSET + note_idx * memory::NOTE_MEM_SIZE
19}
20
21// HELPER NOTES
22// ================================================================================================
23
24/// Creates a `P2ANY` note.
25///
26/// A `P2ANY` note carries `assets` and a script that moves the assets into the executing account's
27/// vault.
28///
29/// The created note does not require authentication and can be consumed by any account.
30pub fn create_p2any_note(sender: AccountId, assets: &[Asset]) -> Note {
31    let mut code_body = String::new();
32    for i in 0..assets.len() {
33        if i == 0 {
34            // first asset (dest_ptr is already on stack)
35            code_body.push_str(
36                "
37                # add first asset
38                
39                padw dup.4 mem_loadw
40                padw swapw padw padw swapdw
41                call.wallet::receive_asset      
42                dropw movup.12
43                # => [dest_ptr, pad(12)]
44                ",
45            );
46        } else {
47            code_body.push_str(
48                "
49                # add next asset
50
51                add.4 dup movdn.13
52                padw movup.4 mem_loadw
53                call.wallet::receive_asset
54                dropw movup.12
55                # => [dest_ptr, pad(12)]",
56            );
57        }
58    }
59    code_body.push_str("dropw dropw dropw dropw");
60
61    let code = format!(
62        "
63        use.test::account
64        use.miden::note
65        use.miden::contracts::wallets::basic->wallet
66
67        begin
68            # fetch pointer & number of assets
69            push.0 exec.note::get_assets          # [num_assets, dest_ptr]
70
71            # runtime-check we got the expected count
72            push.{num_assets} assert_eq             # [dest_ptr]
73
74            {code_body}
75            dropw dropw dropw dropw
76        end
77        ",
78        num_assets = assets.len(),
79    );
80
81    NoteBuilder::new(sender, SmallRng::from_seed([0; 32]))
82        .add_assets(assets.iter().copied())
83        .code(code)
84        .build(&TransactionKernel::testing_assembler_with_mock_account())
85        .expect("generated note script should compile")
86}
87
88/// Creates a `SPAWN` note.
89///
90///  A `SPAWN` note contains a note script that creates all `output_notes` that get passed as a
91///  parameter.
92pub fn create_spawn_note(sender_id: AccountId, output_notes: Vec<&Note>) -> anyhow::Result<Note> {
93    let note_code = note_script_that_creates_notes(output_notes);
94
95    let note = NoteBuilder::new(sender_id, SmallRng::from_os_rng())
96        .code(note_code)
97        .build(&TransactionKernel::testing_assembler_with_mock_account())?;
98
99    Ok(note)
100}
101
102/// Returns the code for a note that creates all notes in `output_notes`
103fn note_script_that_creates_notes(output_notes: Vec<&Note>) -> String {
104    let mut out = String::from("use.miden::tx\nuse.test::account\n\nbegin\n");
105
106    for (idx, note) in output_notes.iter().enumerate() {
107        if idx == 0 {
108            out.push_str("padw padw\n");
109        } else {
110            out.push_str("dropw dropw dropw\n");
111        }
112        let assets_str = prepare_assets(note.assets());
113        out.push_str(&format!(
114            " push.{recipient}
115              push.{hint}
116              push.{note_type}
117              push.{aux}
118              push.{tag}
119              call.tx::create_note\n",
120            recipient = word_to_masm_push_string(&note.recipient().digest()),
121            hint = Felt::from(note.metadata().execution_hint()),
122            note_type = note.metadata().note_type() as u8,
123            aux = note.metadata().aux(),
124            tag = note.metadata().tag(),
125        ));
126
127        for asset in assets_str {
128            out.push_str(&format!(
129                " push.{asset}
130                  call.tx::add_asset_to_note\n",
131            ));
132        }
133    }
134
135    out.push_str("repeat.5 dropw end\nend");
136    out
137}