miden_testing/tx_context/
context.rs1use alloc::borrow::ToOwned;
2use alloc::collections::BTreeSet;
3use alloc::rc::Rc;
4use alloc::sync::Arc;
5use alloc::vec::Vec;
6
7use miden_lib::transaction::TransactionKernel;
8use miden_objects::account::{Account, AccountId};
9use miden_objects::assembly::debuginfo::{SourceLanguage, Uri};
10use miden_objects::assembly::{SourceManager, SourceManagerSync};
11use miden_objects::block::{BlockHeader, BlockNumber};
12use miden_objects::note::Note;
13use miden_objects::transaction::{
14 ExecutedTransaction,
15 InputNote,
16 InputNotes,
17 PartialBlockchain,
18 TransactionArgs,
19 TransactionInputs,
20};
21use miden_processor::{
22 AdviceInputs,
23 ExecutionError,
24 FutureMaybeSend,
25 MastForest,
26 MastForestStore,
27 Process,
28 Word,
29};
30use miden_tx::auth::BasicAuthenticator;
31use miden_tx::{
32 DataStore,
33 DataStoreError,
34 TransactionExecutor,
35 TransactionExecutorError,
36 TransactionMastStore,
37};
38use rand_chacha::ChaCha20Rng;
39
40use crate::MockHost;
41use crate::executor::CodeExecutor;
42use crate::tx_context::builder::MockAuthenticator;
43
44pub struct TransactionContext {
52 pub(super) expected_output_notes: Vec<Note>,
53 pub(super) tx_args: TransactionArgs,
54 pub(super) tx_inputs: TransactionInputs,
55 pub(super) mast_store: TransactionMastStore,
56 pub(super) advice_inputs: AdviceInputs,
57 pub(super) authenticator: Option<MockAuthenticator>,
58 pub(super) source_manager: Arc<dyn SourceManagerSync>,
59}
60
61impl TransactionContext {
62 pub fn execute_code(&self, code: &str) -> Result<Process, ExecutionError> {
82 let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(
83 &self.tx_inputs,
84 &self.tx_args,
85 Some(self.advice_inputs.clone()),
86 )
87 .expect("error initializing transaction inputs");
88
89 let virtual_source_file = self.source_manager.load(
91 SourceLanguage::Masm,
92 Uri::new("_tx_context_code"),
93 code.to_owned(),
94 );
95
96 let assembler = TransactionKernel::with_mock_libraries(self.source_manager.clone())
97 .with_debug_mode(true);
98 let program = assembler
99 .with_debug_mode(true)
100 .assemble_program(virtual_source_file)
101 .expect("code was not well formed");
102
103 let mast_store = Rc::new(TransactionMastStore::new());
104
105 mast_store.insert(program.mast_forest().clone());
106 mast_store.insert(TransactionKernel::library().mast_forest().clone());
107 mast_store.load_account_code(self.account().code());
108 for acc_inputs in self.tx_args.foreign_account_inputs() {
109 mast_store.load_account_code(acc_inputs.code());
110 }
111
112 let advice_inputs = advice_inputs.into_advice_inputs();
113 CodeExecutor::new(
114 MockHost::new(
115 self.tx_inputs.account().into(),
116 &advice_inputs,
117 mast_store,
118 self.tx_args.to_foreign_account_code_commitments(),
119 )
120 .with_source_manager(self.source_manager()),
121 )
122 .stack_inputs(stack_inputs)
123 .extend_advice_inputs(advice_inputs)
124 .execute_program(program)
125 }
126
127 pub async fn execute(self) -> Result<ExecutedTransaction, TransactionExecutorError> {
129 let account_id = self.account().id();
130 let block_num = self.tx_inputs().block_header().block_num();
131 let notes = self.tx_inputs().input_notes().clone();
132 let tx_args = self.tx_args().clone();
133
134 let mut tx_executor = TransactionExecutor::new(&self)
135 .with_source_manager(self.source_manager.clone())
136 .with_debug_mode();
137 if let Some(authenticator) = self.authenticator() {
138 tx_executor = tx_executor.with_authenticator(authenticator);
139 }
140
141 tx_executor.execute_transaction(account_id, block_num, notes, tx_args).await
142 }
143
144 pub fn execute_blocking(self) -> Result<ExecutedTransaction, TransactionExecutorError> {
150 tokio::runtime::Builder::new_current_thread()
151 .build()
152 .unwrap()
153 .block_on(self.execute())
154 }
155
156 pub fn account(&self) -> &Account {
157 self.tx_inputs.account()
158 }
159
160 pub fn expected_output_notes(&self) -> &[Note] {
161 &self.expected_output_notes
162 }
163
164 pub fn tx_args(&self) -> &TransactionArgs {
165 &self.tx_args
166 }
167
168 pub fn input_notes(&self) -> &InputNotes<InputNote> {
169 self.tx_inputs.input_notes()
170 }
171
172 pub fn set_tx_args(&mut self, tx_args: TransactionArgs) {
173 self.tx_args = tx_args;
174 }
175
176 pub fn tx_inputs(&self) -> &TransactionInputs {
177 &self.tx_inputs
178 }
179
180 pub fn authenticator(&self) -> Option<&BasicAuthenticator<ChaCha20Rng>> {
181 self.authenticator.as_ref()
182 }
183
184 pub fn source_manager(&self) -> Arc<dyn SourceManagerSync> {
186 Arc::clone(&self.source_manager)
187 }
188}
189
190impl DataStore for TransactionContext {
191 fn get_transaction_inputs(
192 &self,
193 account_id: AccountId,
194 _ref_blocks: BTreeSet<BlockNumber>,
195 ) -> impl FutureMaybeSend<
196 Result<(Account, Option<Word>, BlockHeader, PartialBlockchain), DataStoreError>,
197 > {
198 assert_eq!(account_id, self.account().id());
199 let (account, seed, header, mmr, _) = self.tx_inputs.clone().into_parts();
200 async move { Ok((account, seed, header, mmr)) }
201 }
202}
203
204impl MastForestStore for TransactionContext {
205 fn get(&self, procedure_hash: &Word) -> Option<Arc<MastForest>> {
206 self.mast_store.get(procedure_hash)
207 }
208}