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 &mast_store,
139 script_mast_store,
140 account_procedure_index_map,
141 );
142
143 let advice_inputs = advice_inputs.into_advice_inputs();
144
145 let (stack_outputs, proof) = prove_sync(
146 &TransactionKernel::main(),
147 stack_inputs,
148 advice_inputs.clone(),
149 &mut host,
150 ExecutionOptions::default(),
151 self.proof_options.clone(),
152 )
153 .map_err(TransactionProverError::TransactionProgramExecutionFailed)?;
154
155 let (account_patch, input_notes, output_notes) = host.into_parts();
157 let tx_outputs =
158 TransactionKernel::from_transaction_parts(&stack_outputs, &advice_inputs, output_notes)
159 .map_err(TransactionProverError::TransactionOutputConstructionFailed)?;
160
161 self.build_proven_transaction(
162 &input_notes,
163 tx_outputs,
164 account_patch,
165 partial_account,
166 ref_block.block_num(),
167 ref_block.commitment(),
168 proof,
169 )
170 }
171}
172
173#[cfg(any(feature = "testing", test))]
174impl LocalTransactionProver {
175 pub fn prove_dummy(
176 &self,
177 executed_transaction: miden_protocol::transaction::ExecutedTransaction,
178 ) -> Result<ProvenTransaction, TransactionProverError> {
179 let (tx_inputs, tx_outputs, account_patch, _) = executed_transaction.into_parts();
180
181 let (partial_account, ref_block, _, input_notes, _) = tx_inputs.into_parts();
182
183 self.build_proven_transaction(
184 &input_notes,
185 tx_outputs,
186 account_patch,
187 partial_account,
188 ref_block.block_num(),
189 ref_block.commitment(),
190 ExecutionProof::new_dummy(),
191 )
192 }
193}