1use alloc::sync::Arc;
2use alloc::vec::Vec;
3
4use miden_protocol::account::delta::AccountUpdateDetails;
5use miden_protocol::account::{AccountDelta, PartialAccount};
6use miden_protocol::asset::Asset;
7use miden_protocol::block::BlockNumber;
8use miden_protocol::transaction::{
9 InputNote,
10 InputNotes,
11 OutputNote,
12 ProvenTransaction,
13 ProvenTransactionBuilder,
14 TransactionInputs,
15 TransactionKernel,
16 TransactionOutputs,
17};
18pub use miden_prover::ProvingOptions;
19use miden_prover::{ExecutionProof, Word, prove};
20
21use super::TransactionProverError;
22use crate::host::{AccountProcedureIndexMap, ScriptMastForestStore};
23
24mod prover_host;
25pub use prover_host::TransactionProverHost;
26
27mod mast_store;
28pub use mast_store::TransactionMastStore;
29
30pub struct LocalTransactionProver {
35 mast_store: Arc<TransactionMastStore>,
36 proof_options: ProvingOptions,
37}
38
39impl LocalTransactionProver {
40 pub fn new(proof_options: ProvingOptions) -> Self {
42 Self {
43 mast_store: Arc::new(TransactionMastStore::new()),
44 proof_options,
45 }
46 }
47
48 fn build_proven_transaction(
49 &self,
50 input_notes: &InputNotes<InputNote>,
51 tx_outputs: TransactionOutputs,
52 pre_fee_account_delta: AccountDelta,
53 account: PartialAccount,
54 ref_block_num: BlockNumber,
55 ref_block_commitment: Word,
56 proof: ExecutionProof,
57 ) -> Result<ProvenTransaction, TransactionProverError> {
58 let output_notes: Vec<_> = tx_outputs.output_notes.iter().map(OutputNote::shrink).collect();
60
61 let pre_fee_delta_commitment: Word = pre_fee_account_delta.to_commitment();
64
65 let builder = ProvenTransactionBuilder::new(
66 account.id(),
67 account.initial_commitment(),
68 tx_outputs.account.commitment(),
69 pre_fee_delta_commitment,
70 ref_block_num,
71 ref_block_commitment,
72 tx_outputs.fee,
73 tx_outputs.expiration_block_num,
74 proof,
75 )
76 .add_input_notes(input_notes)
77 .add_output_notes(output_notes);
78
79 let mut post_fee_account_delta = pre_fee_account_delta;
81 post_fee_account_delta
82 .vault_mut()
83 .remove_asset(Asset::from(tx_outputs.fee))
84 .map_err(TransactionProverError::RemoveFeeAssetFromDelta)?;
85
86 let builder = match account.has_public_state() {
87 true => {
88 let account_update_details = AccountUpdateDetails::Delta(post_fee_account_delta);
89 builder.account_update_details(account_update_details)
90 },
91 false => builder,
92 };
93
94 builder.build().map_err(TransactionProverError::ProvenTransactionBuildFailed)
95 }
96
97 pub fn prove(
98 &self,
99 tx_inputs: impl Into<TransactionInputs>,
100 ) -> Result<ProvenTransaction, TransactionProverError> {
101 let tx_inputs = tx_inputs.into();
102 let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&tx_inputs);
103
104 self.mast_store.load_account_code(tx_inputs.account().code());
105 for account_code in tx_inputs.foreign_account_code() {
106 self.mast_store.load_account_code(account_code);
107 }
108
109 let script_mast_store = ScriptMastForestStore::new(
110 tx_inputs.tx_script(),
111 tx_inputs.input_notes().iter().map(|n| n.note().script()),
112 );
113
114 let account_procedure_index_map = AccountProcedureIndexMap::new(
115 tx_inputs.foreign_account_code().iter().chain([tx_inputs.account().code()]),
116 );
117
118 let (partial_account, ref_block, _, input_notes, _) = tx_inputs.into_parts();
119 let mut host = TransactionProverHost::new(
120 &partial_account,
121 input_notes,
122 self.mast_store.as_ref(),
123 script_mast_store,
124 account_procedure_index_map,
125 );
126
127 let advice_inputs = advice_inputs.into_advice_inputs();
128
129 let (stack_outputs, proof) = prove(
130 &TransactionKernel::main(),
131 stack_inputs,
132 advice_inputs.clone(),
133 &mut host,
134 self.proof_options.clone(),
135 )
136 .map_err(TransactionProverError::TransactionProgramExecutionFailed)?;
137
138 let (pre_fee_account_delta, input_notes, output_notes) = host.into_parts();
142 let tx_outputs =
143 TransactionKernel::from_transaction_parts(&stack_outputs, &advice_inputs, output_notes)
144 .map_err(TransactionProverError::TransactionOutputConstructionFailed)?;
145
146 self.build_proven_transaction(
147 &input_notes,
148 tx_outputs,
149 pre_fee_account_delta,
150 partial_account,
151 ref_block.block_num(),
152 ref_block.commitment(),
153 proof,
154 )
155 }
156}
157
158impl Default for LocalTransactionProver {
159 fn default() -> Self {
160 Self {
161 mast_store: Arc::new(TransactionMastStore::new()),
162 proof_options: Default::default(),
163 }
164 }
165}
166
167#[cfg(any(feature = "testing", test))]
168impl LocalTransactionProver {
169 pub fn prove_dummy(
170 &self,
171 executed_transaction: miden_protocol::transaction::ExecutedTransaction,
172 ) -> Result<ProvenTransaction, TransactionProverError> {
173 let (tx_inputs, tx_outputs, account_delta, _) = executed_transaction.into_parts();
174
175 let (partial_account, ref_block, _, input_notes, _) = tx_inputs.into_parts();
176
177 self.build_proven_transaction(
178 &input_notes,
179 tx_outputs,
180 account_delta,
181 partial_account,
182 ref_block.block_num(),
183 ref_block.commitment(),
184 ExecutionProof::new_dummy(),
185 )
186 }
187}