1use alloc::vec::Vec;
2
3use miden_processor::ExecutionOptions;
4use miden_protocol::account::{AccountPatch, AccountUpdateDetails, PartialAccount};
5use miden_protocol::block::BlockNumber;
6use miden_protocol::transaction::{
7 InputNote,
8 InputNotes,
9 ProvenTransaction,
10 TransactionInputs,
11 TransactionKernel,
12 TransactionOutputs,
13 TxAccountUpdate,
14};
15use miden_prover::HashFunction::Poseidon2;
16pub use miden_prover::ProvingOptions;
17use miden_prover::{ExecutionProof, Word, prove_sync};
18
19use super::TransactionProverError;
20use crate::host::{AccountProcedureIndexMap, ScriptMastForestStore};
21
22mod prover_host;
23pub use prover_host::TransactionProverHost;
24
25mod mast_store;
26pub use mast_store::TransactionMastStore;
27
28#[derive(Debug, Clone)]
37pub struct LocalTransactionProver {
38 proof_options: ProvingOptions,
39}
40
41impl Default for LocalTransactionProver {
42 fn default() -> Self {
43 Self {
44 proof_options: ProvingOptions::new(Poseidon2),
45 }
46 }
47}
48
49impl LocalTransactionProver {
50 pub fn new(proof_options: ProvingOptions) -> Self {
52 Self { proof_options }
53 }
54
55 fn build_proven_transaction(
56 &self,
57 input_notes: &InputNotes<InputNote>,
58 tx_outputs: TransactionOutputs,
59 account_patch: AccountPatch,
60 account: PartialAccount,
61 ref_block_num: BlockNumber,
62 ref_block_commitment: Word,
63 proof: ExecutionProof,
64 ) -> Result<ProvenTransaction, TransactionProverError> {
65 let expiration_block_num = tx_outputs.expiration_block_num();
66 let (account_header, output_notes) = tx_outputs.into_parts();
67
68 let output_notes: Vec<_> = output_notes
70 .into_iter()
71 .map(|note| note.into_output_note())
72 .collect::<Result<Vec<_>, _>>()
73 .map_err(TransactionProverError::OutputNoteShrinkFailed)?;
74
75 let patch_commitment: Word = account_patch.to_commitment();
78
79 let account_update_details = if account.id().is_public() {
80 AccountUpdateDetails::Public(account_patch)
81 } else {
82 AccountUpdateDetails::Private
83 };
84
85 let account_update = TxAccountUpdate::new(
86 account.id(),
87 account.initial_commitment(),
88 account_header.to_commitment(),
89 patch_commitment,
90 account_update_details,
91 )
92 .map_err(TransactionProverError::ProvenTransactionBuildFailed)?;
93
94 ProvenTransaction::new(
95 account_update,
96 input_notes.iter(),
97 output_notes,
98 ref_block_num,
99 ref_block_commitment,
100 expiration_block_num,
101 proof,
102 )
103 .map_err(TransactionProverError::ProvenTransactionBuildFailed)
104 }
105
106 pub fn prove(
107 &self,
108 tx_inputs: impl Into<TransactionInputs>,
109 ) -> Result<ProvenTransaction, TransactionProverError> {
110 let tx_inputs = tx_inputs.into();
111 let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&tx_inputs);
112
113 let mast_store = TransactionMastStore::new();
120 mast_store.load_account_code(tx_inputs.account().code());
121 for account_code in tx_inputs.foreign_account_code() {
122 mast_store.load_account_code(account_code);
123 }
124
125 let script_mast_store = ScriptMastForestStore::new(
126 tx_inputs.tx_script(),
127 tx_inputs.input_notes().iter().map(|n| n.note().script()),
128 );
129
130 let account_procedure_index_map = AccountProcedureIndexMap::new(
131 tx_inputs.foreign_account_code().iter().chain([tx_inputs.account().code()]),
132 );
133
134 let (partial_account, ref_block, _, input_notes, _) = tx_inputs.into_parts();
135 let mut host = TransactionProverHost::new(
136 &partial_account,
137 input_notes,
138 ref_block.commitment(),
139 &mast_store,
140 script_mast_store,
141 account_procedure_index_map,
142 );
143
144 let advice_inputs = advice_inputs.into_advice_inputs();
145
146 let (stack_outputs, proof) = prove_sync(
147 &TransactionKernel::main(),
148 stack_inputs,
149 advice_inputs.clone(),
150 &mut host,
151 ExecutionOptions::default(),
152 self.proof_options.clone(),
153 )
154 .map_err(TransactionProverError::TransactionProgramExecutionFailed)?;
155
156 let (account_patch, input_notes, output_notes) = host.into_parts();
158 let tx_outputs =
159 TransactionKernel::from_transaction_parts(&stack_outputs, &advice_inputs, output_notes)
160 .map_err(TransactionProverError::TransactionOutputConstructionFailed)?;
161
162 self.build_proven_transaction(
163 &input_notes,
164 tx_outputs,
165 account_patch,
166 partial_account,
167 ref_block.block_num(),
168 ref_block.commitment(),
169 proof,
170 )
171 }
172}
173
174#[cfg(any(feature = "testing", test))]
175impl LocalTransactionProver {
176 pub fn prove_dummy(
177 &self,
178 executed_transaction: miden_protocol::transaction::ExecutedTransaction,
179 ) -> Result<ProvenTransaction, TransactionProverError> {
180 let (tx_inputs, tx_outputs, account_patch, _) = executed_transaction.into_parts();
181
182 let (partial_account, ref_block, _, input_notes, _) = tx_inputs.into_parts();
183
184 self.build_proven_transaction(
185 &input_notes,
186 tx_outputs,
187 account_patch,
188 partial_account,
189 ref_block.block_num(),
190 ref_block.commitment(),
191 ExecutionProof::new_dummy(),
192 )
193 }
194}