miden_testing/tx_context/
builder.rs1use alloc::collections::BTreeMap;
5use alloc::sync::Arc;
6use alloc::vec::Vec;
7
8use anyhow::Context;
9use miden_processor::advice::AdviceInputs;
10use miden_processor::{Felt, Word};
11use miden_protocol::EMPTY_WORD;
12use miden_protocol::account::auth::{PublicKeyCommitment, Signature};
13use miden_protocol::account::{Account, AccountHeader, AccountId};
14use miden_protocol::assembly::DefaultSourceManager;
15use miden_protocol::assembly::debuginfo::SourceManagerSync;
16use miden_protocol::block::account_tree::AccountWitness;
17use miden_protocol::note::{Note, NoteId, NoteScript};
18use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE;
19use miden_protocol::testing::noop_auth_component::NoopAuthComponent;
20use miden_protocol::transaction::{
21 RawOutputNote,
22 TransactionArgs,
23 TransactionInputs,
24 TransactionScript,
25};
26use miden_standards::testing::account_component::IncrNonceAuthComponent;
27use miden_standards::testing::mock_account::MockAccountExt;
28use miden_tx::TransactionMastStore;
29use miden_tx::auth::BasicAuthenticator;
30
31use super::TransactionContext;
32use crate::{MockChain, MockChainNote};
33
34pub struct TransactionContextBuilder {
71 source_manager: Arc<dyn SourceManagerSync>,
72 account: Account,
73 advice_inputs: AdviceInputs,
74 authenticator: Option<BasicAuthenticator>,
75 expected_output_notes: Vec<Note>,
76 foreign_account_inputs: BTreeMap<AccountId, (Account, AccountWitness)>,
77 input_notes: Vec<Note>,
78 tx_script: Option<TransactionScript>,
79 tx_script_args: Word,
80 note_args: BTreeMap<NoteId, Word>,
81 tx_inputs: Option<TransactionInputs>,
82 auth_args: Word,
83 signatures: Vec<(PublicKeyCommitment, Word, Signature)>,
84 note_scripts: BTreeMap<Word, NoteScript>,
85 is_lazy_loading_enabled: bool,
86 is_debug_mode_enabled: bool,
87}
88
89impl TransactionContextBuilder {
90 pub fn new(account: Account) -> Self {
91 Self {
92 source_manager: Arc::new(DefaultSourceManager::default()),
93 account,
94 input_notes: Vec::new(),
95 expected_output_notes: Vec::new(),
96 tx_script: None,
97 tx_script_args: EMPTY_WORD,
98 authenticator: None,
99 advice_inputs: Default::default(),
100 tx_inputs: None,
101 note_args: BTreeMap::new(),
102 foreign_account_inputs: BTreeMap::new(),
103 auth_args: EMPTY_WORD,
104 signatures: Vec::new(),
105 note_scripts: BTreeMap::new(),
106 is_lazy_loading_enabled: true,
107 is_debug_mode_enabled: cfg!(feature = "tx_context_debug"),
108 }
109 }
110
111 pub fn with_existing_mock_account() -> Self {
121 Self::new(Account::mock(
122 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE,
123 IncrNonceAuthComponent,
124 ))
125 }
126
127 pub fn with_noop_auth_account() -> Self {
129 let account =
130 Account::mock(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, NoopAuthComponent);
131
132 Self::new(account)
133 }
134
135 pub fn with_fungible_faucet(acct_id: u128) -> Self {
137 let account = Account::mock_fungible_faucet(acct_id);
138 Self::new(account)
139 }
140
141 pub fn with_non_fungible_faucet(acct_id: u128) -> Self {
143 let account = Account::mock_non_fungible_faucet(acct_id);
144 Self::new(account)
145 }
146
147 pub fn extend_advice_inputs(mut self, advice_inputs: AdviceInputs) -> Self {
149 self.advice_inputs.extend(advice_inputs);
150 self
151 }
152
153 pub fn extend_advice_map(
155 mut self,
156 map_entries: impl IntoIterator<Item = (Word, Vec<Felt>)>,
157 ) -> Self {
158 self.advice_inputs.map.extend(map_entries);
159 self
160 }
161
162 pub fn authenticator(mut self, authenticator: Option<BasicAuthenticator>) -> Self {
164 self.authenticator = authenticator;
165 self
166 }
167
168 pub fn foreign_accounts(
170 mut self,
171 inputs: impl IntoIterator<Item = (Account, AccountWitness)>,
172 ) -> Self {
173 self.foreign_account_inputs.extend(
174 inputs.into_iter().map(|(account, witness)| (account.id(), (account, witness))),
175 );
176 self
177 }
178
179 pub fn extend_input_notes(mut self, input_notes: Vec<Note>) -> Self {
181 self.input_notes.extend(input_notes);
182 self
183 }
184
185 pub fn tx_script(mut self, tx_script: TransactionScript) -> Self {
187 self.tx_script = Some(tx_script);
188 self
189 }
190
191 pub fn tx_script_args(mut self, tx_script_args: Word) -> Self {
193 self.tx_script_args = tx_script_args;
194 self
195 }
196
197 pub fn auth_args(mut self, auth_args: Word) -> Self {
199 self.auth_args = auth_args;
200 self
201 }
202
203 pub fn tx_inputs(mut self, tx_inputs: TransactionInputs) -> Self {
205 assert_eq!(
206 AccountHeader::from(&self.account),
207 tx_inputs.account().into(),
208 "account in context and account provided via tx inputs are not the same account"
209 );
210 self.tx_inputs = Some(tx_inputs);
211 self
212 }
213
214 pub fn disable_lazy_loading(mut self) -> Self {
219 self.is_lazy_loading_enabled = false;
220 self
221 }
222
223 pub fn disable_debug_mode(mut self) -> Self {
228 self.is_debug_mode_enabled = false;
229 self
230 }
231
232 pub fn extend_note_args(mut self, note_args: BTreeMap<NoteId, Word>) -> Self {
234 self.note_args.extend(note_args);
235 self
236 }
237
238 pub fn extend_expected_output_notes(mut self, output_notes: Vec<RawOutputNote>) -> Self {
240 let output_notes = output_notes.into_iter().filter_map(|n| match n {
241 RawOutputNote::Full(note) => Some(note),
242 RawOutputNote::Partial(_) => None,
243 });
244
245 self.expected_output_notes.extend(output_notes);
246 self
247 }
248
249 pub fn with_source_manager(mut self, source_manager: Arc<dyn SourceManagerSync>) -> Self {
254 self.source_manager = source_manager.clone();
255 self
256 }
257
258 pub fn add_signature(
260 mut self,
261 pub_key: PublicKeyCommitment,
262 message: Word,
263 signature: Signature,
264 ) -> Self {
265 self.signatures.push((pub_key, message, signature));
266 self
267 }
268
269 pub fn add_note_script(mut self, script: NoteScript) -> Self {
271 self.note_scripts.insert(script.root(), script);
272 self
273 }
274
275 pub fn build(self) -> anyhow::Result<TransactionContext> {
280 let mut tx_inputs = match self.tx_inputs {
281 Some(tx_inputs) => tx_inputs,
282 None => {
283 let mut builder = MockChain::builder();
287 for i in self.input_notes {
288 builder.add_output_note(RawOutputNote::Full(i));
289 }
290 let mut mock_chain = builder.build()?;
291
292 mock_chain.prove_next_block().context("failed to prove first block")?;
293 mock_chain.prove_next_block().context("failed to prove second block")?;
294
295 let input_note_ids: Vec<NoteId> =
296 mock_chain.committed_notes().values().map(MockChainNote::id).collect();
297
298 mock_chain
299 .get_transaction_inputs(&self.account, &input_note_ids, &[])
300 .context("failed to get transaction inputs from mock chain")?
301 },
302 };
303
304 let mut tx_args = TransactionArgs::default().with_note_args(self.note_args);
305
306 tx_args = if let Some(tx_script) = self.tx_script {
307 tx_args.with_tx_script_and_args(tx_script, self.tx_script_args)
308 } else {
309 tx_args
310 };
311 tx_args = tx_args.with_auth_args(self.auth_args);
312 tx_args.extend_advice_inputs(self.advice_inputs.clone());
313 tx_args.extend_output_note_recipients(self.expected_output_notes.clone());
314
315 for (public_key_commitment, message, signature) in self.signatures {
316 tx_args.add_signature(public_key_commitment, message, signature);
317 }
318
319 tx_inputs.set_tx_args(tx_args);
320
321 let mast_store = {
322 let mast_forest_store = TransactionMastStore::new();
323 mast_forest_store.load_account_code(tx_inputs.account().code());
324
325 for (account, _) in self.foreign_account_inputs.values() {
326 mast_forest_store.insert(account.code().mast());
327 }
328
329 mast_forest_store
330 };
331
332 Ok(TransactionContext {
333 account: self.account,
334 expected_output_notes: self.expected_output_notes,
335 foreign_account_inputs: self.foreign_account_inputs,
336 tx_inputs,
337 mast_store,
338 authenticator: self.authenticator,
339 source_manager: self.source_manager,
340 note_scripts: self.note_scripts,
341 is_lazy_loading_enabled: self.is_lazy_loading_enabled,
342 is_debug_mode_enabled: self.is_debug_mode_enabled,
343 })
344 }
345}
346
347impl Default for TransactionContextBuilder {
348 fn default() -> Self {
349 Self::with_existing_mock_account()
350 }
351}