miden_testing/tx_context/
context.rs1#[cfg(feature = "async")]
2use alloc::boxed::Box;
3use alloc::{borrow::ToOwned, collections::BTreeSet, rc::Rc, sync::Arc, vec::Vec};
4
5use miden_lib::transaction::TransactionKernel;
6use miden_objects::{
7 account::{Account, AccountId},
8 assembly::{Assembler, SourceManager},
9 block::{BlockHeader, BlockNumber},
10 note::Note,
11 transaction::{
12 ExecutedTransaction, InputNote, InputNotes, PartialBlockchain, TransactionArgs,
13 TransactionInputs,
14 },
15};
16use miden_tx::{
17 DataStore, DataStoreError, TransactionExecutor, TransactionExecutorError, TransactionMastStore,
18 auth::{BasicAuthenticator, TransactionAuthenticator},
19};
20use rand_chacha::ChaCha20Rng;
21use vm_processor::{
22 AdviceInputs, Digest, ExecutionError, MastForest, MastForestStore, Process, Word,
23};
24use winter_maybe_async::*;
25
26use crate::{MockHost, executor::CodeExecutor, tx_context::builder::MockAuthenticator};
27
28pub struct TransactionContext {
36 pub(super) expected_output_notes: Vec<Note>,
37 pub(super) tx_args: TransactionArgs,
38 pub(super) tx_inputs: TransactionInputs,
39 pub(super) mast_store: TransactionMastStore,
40 pub(super) advice_inputs: AdviceInputs,
41 pub(super) authenticator: Option<MockAuthenticator>,
42 pub(super) source_manager: Arc<dyn SourceManager>,
43}
44
45impl TransactionContext {
46 pub fn execute_code_with_assembler(
65 &self,
66 code: &str,
67 assembler: Assembler,
68 ) -> Result<Process, ExecutionError> {
69 let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(
70 &self.tx_inputs,
71 &self.tx_args,
72 Some(self.advice_inputs.clone()),
73 );
74
75 let test_lib = TransactionKernel::kernel_as_library();
76
77 let source_manager = assembler.source_manager();
78
79 let virtual_source_file = source_manager.load("_tx_context_code", code.to_owned());
81
82 let program = assembler
83 .with_debug_mode(true)
84 .assemble_program(virtual_source_file)
85 .expect("code was not well formed");
86
87 let mast_store = Rc::new(TransactionMastStore::new());
88
89 mast_store.insert(program.mast_forest().clone());
90 mast_store.insert(test_lib.mast_forest().clone());
91 mast_store.load_account_code(self.account().code());
92 for acc_inputs in self.tx_args.foreign_account_inputs() {
93 mast_store.load_account_code(acc_inputs.code());
94 }
95
96 CodeExecutor::new(MockHost::new(
97 self.tx_inputs.account().into(),
98 advice_inputs,
99 mast_store,
100 self.tx_args.foreign_account_code_commitments(),
101 ))
102 .stack_inputs(stack_inputs)
103 .execute_program(program, source_manager)
104 }
105
106 pub fn execute_code(&self, code: &str) -> Result<Process, ExecutionError> {
110 let assembler = TransactionKernel::testing_assembler();
111 self.execute_code_with_assembler(code, assembler)
112 }
113
114 #[allow(clippy::arc_with_non_send_sync)]
116 #[maybe_async]
117 pub fn execute(self) -> Result<ExecutedTransaction, TransactionExecutorError> {
118 let account_id = self.account().id();
119 let block_num = self.tx_inputs().block_header().block_num();
120 let notes = self.tx_inputs().input_notes().clone();
121 let tx_args = self.tx_args().clone();
122 let authenticator = self.authenticator().map(|x| x as &dyn TransactionAuthenticator);
123
124 let source_manager = Arc::clone(&self.source_manager);
125 let tx_executor = TransactionExecutor::new(&self, authenticator).with_debug_mode();
126
127 maybe_await!(tx_executor.execute_transaction(
128 account_id,
129 block_num,
130 notes,
131 tx_args,
132 source_manager
133 ))
134 }
135
136 pub fn account(&self) -> &Account {
137 self.tx_inputs.account()
138 }
139
140 pub fn expected_output_notes(&self) -> &[Note] {
141 &self.expected_output_notes
142 }
143
144 pub fn tx_args(&self) -> &TransactionArgs {
145 &self.tx_args
146 }
147
148 pub fn input_notes(&self) -> &InputNotes<InputNote> {
149 self.tx_inputs.input_notes()
150 }
151
152 pub fn set_tx_args(&mut self, tx_args: TransactionArgs) {
153 self.tx_args = tx_args;
154 }
155
156 pub fn tx_inputs(&self) -> &TransactionInputs {
157 &self.tx_inputs
158 }
159
160 pub fn authenticator(&self) -> Option<&BasicAuthenticator<ChaCha20Rng>> {
161 self.authenticator.as_ref()
162 }
163
164 pub fn source_manager(&self) -> Arc<dyn SourceManager> {
166 Arc::clone(&self.source_manager)
167 }
168}
169
170#[maybe_async_trait]
171impl DataStore for TransactionContext {
172 #[maybe_async]
173 fn get_transaction_inputs(
174 &self,
175 account_id: AccountId,
176 _ref_blocks: BTreeSet<BlockNumber>,
177 ) -> Result<(Account, Option<Word>, BlockHeader, PartialBlockchain), DataStoreError> {
178 assert_eq!(account_id, self.account().id());
179 let (account, seed, header, mmr, _) = self.tx_inputs.clone().into_parts();
180
181 Ok((account, seed, header, mmr))
182 }
183}
184
185impl MastForestStore for TransactionContext {
186 fn get(&self, procedure_hash: &Digest) -> Option<Arc<MastForest>> {
187 self.mast_store.get(procedure_hash)
188 }
189}