1use alloc::sync::Arc;
2use alloc::vec::Vec;
3
4use miden_lib::transaction::TransactionKernel;
5use miden_objects::account::delta::AccountUpdateDetails;
6use miden_objects::asset::Asset;
7use miden_objects::transaction::{
8 OutputNote,
9 ProvenTransaction,
10 ProvenTransactionBuilder,
11 TransactionWitness,
12};
13pub use miden_prover::ProvingOptions;
14use miden_prover::prove;
15
16use super::TransactionProverError;
17use crate::host::{AccountProcedureIndexMap, ScriptMastForestStore};
18
19mod prover_host;
20pub use prover_host::TransactionProverHost;
21
22mod mast_store;
23pub use mast_store::TransactionMastStore;
24
25pub struct LocalTransactionProver {
30 mast_store: Arc<TransactionMastStore>,
31 proof_options: ProvingOptions,
32}
33
34impl LocalTransactionProver {
35 pub fn new(proof_options: ProvingOptions) -> Self {
37 Self {
38 mast_store: Arc::new(TransactionMastStore::new()),
39 proof_options,
40 }
41 }
42}
43
44impl Default for LocalTransactionProver {
45 fn default() -> Self {
46 Self {
47 mast_store: Arc::new(TransactionMastStore::new()),
48 proof_options: Default::default(),
49 }
50 }
51}
52
53impl LocalTransactionProver {
54 pub fn prove(
55 &self,
56 tx_witness: TransactionWitness,
57 ) -> Result<ProvenTransaction, TransactionProverError> {
58 let mast_store = self.mast_store.clone();
59 let proof_options = self.proof_options.clone();
60
61 let TransactionWitness { tx_inputs, tx_args, advice_witness } = tx_witness;
62
63 let account = tx_inputs.account();
64 let input_notes = tx_inputs.input_notes();
65 let ref_block_num = tx_inputs.block_header().block_num();
66 let ref_block_commitment = tx_inputs.block_header().commitment();
67
68 let (stack_inputs, advice_inputs) =
70 TransactionKernel::prepare_inputs(&tx_inputs, &tx_args, Some(advice_witness))
71 .map_err(TransactionProverError::ConflictingAdviceMapEntry)?;
72
73 self.mast_store.load_account_code(account.code());
75 for account_inputs in tx_args.foreign_account_inputs() {
76 self.mast_store.load_account_code(account_inputs.code());
77 }
78
79 let script_mast_store = ScriptMastForestStore::new(
80 tx_args.tx_script(),
81 input_notes.iter().map(|n| n.note().script()),
82 );
83
84 let mut host = {
85 let acct_procedure_index_map = AccountProcedureIndexMap::from_transaction_params(
86 &tx_inputs,
87 &tx_args,
88 &advice_inputs,
89 )
90 .map_err(TransactionProverError::TransactionHostCreationFailed)?;
91
92 TransactionProverHost::new(
93 &account.into(),
94 input_notes.clone(),
95 mast_store.as_ref(),
96 script_mast_store,
97 acct_procedure_index_map,
98 )
99 };
100
101 let advice_inputs = advice_inputs.into_advice_inputs();
102
103 let (stack_outputs, proof) = prove(
104 &TransactionKernel::main(),
105 stack_inputs,
106 advice_inputs.clone(),
107 &mut host,
108 proof_options,
109 )
110 .map_err(TransactionProverError::TransactionProgramExecutionFailed)?;
111
112 let (pre_fee_account_delta, output_notes, _tx_progress) = host.into_parts();
116 let tx_outputs =
117 TransactionKernel::from_transaction_parts(&stack_outputs, &advice_inputs, output_notes)
118 .map_err(TransactionProverError::TransactionOutputConstructionFailed)?;
119
120 let output_notes: Vec<_> = tx_outputs.output_notes.iter().map(OutputNote::shrink).collect();
122
123 let pre_fee_delta_commitment = pre_fee_account_delta.to_commitment();
126
127 let builder = ProvenTransactionBuilder::new(
128 account.id(),
129 account.init_commitment(),
130 tx_outputs.account.commitment(),
131 pre_fee_delta_commitment,
132 ref_block_num,
133 ref_block_commitment,
134 tx_outputs.fee,
135 tx_outputs.expiration_block_num,
136 proof,
137 )
138 .add_input_notes(input_notes)
139 .add_output_notes(output_notes);
140
141 let mut post_fee_account_delta = pre_fee_account_delta;
143 post_fee_account_delta
144 .vault_mut()
145 .remove_asset(Asset::from(tx_outputs.fee))
146 .map_err(TransactionProverError::RemoveFeeAssetFromDelta)?;
147
148 let builder = match account.is_onchain() {
150 true => {
151 let account_update_details = if account.is_new() {
152 let mut account = account.clone();
153 account
154 .apply_delta(&post_fee_account_delta)
155 .map_err(TransactionProverError::AccountDeltaApplyFailed)?;
156
157 AccountUpdateDetails::New(account)
158 } else {
159 AccountUpdateDetails::Delta(post_fee_account_delta)
160 };
161
162 builder.account_update_details(account_update_details)
163 },
164 false => builder,
165 };
166
167 builder.build().map_err(TransactionProverError::ProvenTransactionBuildFailed)
168 }
169}