1#[cfg(feature = "async")]
2use alloc::boxed::Box;
3use alloc::{collections::BTreeSet, sync::Arc, vec::Vec};
4
5use miden_lib::transaction::TransactionKernel;
6use miden_objects::{
7 account::delta::AccountUpdateDetails,
8 assembly::DefaultSourceManager,
9 transaction::{OutputNote, ProvenTransaction, ProvenTransactionBuilder, TransactionWitness},
10};
11pub use miden_prover::ProvingOptions;
12use miden_prover::prove;
13use vm_processor::{Digest, MemAdviceProvider};
14use winter_maybe_async::*;
15
16use super::{TransactionHost, TransactionProverError};
17
18mod mast_store;
19pub use mast_store::TransactionMastStore;
20
21#[maybe_async_trait]
27pub trait TransactionProver {
28 #[maybe_async]
35 fn prove(
36 &self,
37 tx_witness: TransactionWitness,
38 ) -> Result<ProvenTransaction, TransactionProverError>;
39}
40
41pub struct LocalTransactionProver {
48 mast_store: Arc<TransactionMastStore>,
49 proof_options: ProvingOptions,
50}
51
52impl LocalTransactionProver {
53 pub fn new(proof_options: ProvingOptions) -> Self {
55 Self {
56 mast_store: Arc::new(TransactionMastStore::new()),
57 proof_options,
58 }
59 }
60}
61
62impl Default for LocalTransactionProver {
63 fn default() -> Self {
64 Self {
65 mast_store: Arc::new(TransactionMastStore::new()),
66 proof_options: Default::default(),
67 }
68 }
69}
70
71#[maybe_async_trait]
72impl TransactionProver for LocalTransactionProver {
73 #[maybe_async]
74 fn prove(
75 &self,
76 tx_witness: TransactionWitness,
77 ) -> Result<ProvenTransaction, TransactionProverError> {
78 let TransactionWitness { tx_inputs, tx_args, advice_witness } = tx_witness;
79
80 let account = tx_inputs.account();
81 let input_notes = tx_inputs.input_notes();
82 let ref_block_num = tx_inputs.block_header().block_num();
83 let ref_block_commitment = tx_inputs.block_header().commitment();
84
85 let (stack_inputs, advice_inputs) =
87 TransactionKernel::prepare_inputs(&tx_inputs, &tx_args, Some(advice_witness))
88 .map_err(TransactionProverError::InvalidTransactionInputs)?;
89 let advice_provider: MemAdviceProvider = advice_inputs.into();
90
91 self.mast_store.load_transaction_code(account.code(), input_notes, &tx_args);
93
94 let account_code_commitments: BTreeSet<Digest> = tx_args
95 .foreign_account_inputs()
96 .iter()
97 .map(|acc| acc.code().commitment())
98 .collect();
99
100 let mut host: TransactionHost<_> = TransactionHost::new(
101 account.into(),
102 advice_provider,
103 self.mast_store.clone(),
104 None,
105 account_code_commitments,
106 )
107 .map_err(TransactionProverError::TransactionHostCreationFailed)?;
108
109 let source_manager = Arc::new(DefaultSourceManager::default());
113 let (stack_outputs, proof) = maybe_await!(prove(
114 &TransactionKernel::main(),
115 stack_inputs,
116 &mut host,
117 self.proof_options.clone(),
118 source_manager
119 ))
120 .map_err(TransactionProverError::TransactionProgramExecutionFailed)?;
121
122 let (advice_provider, account_delta, output_notes, _signatures, _tx_progress) =
124 host.into_parts();
125 let (_, map, _) = advice_provider.into_parts();
126 let tx_outputs =
127 TransactionKernel::from_transaction_parts(&stack_outputs, &map.into(), output_notes)
128 .map_err(TransactionProverError::TransactionOutputConstructionFailed)?;
129
130 let output_notes: Vec<_> = tx_outputs.output_notes.iter().map(OutputNote::shrink).collect();
132
133 let builder = ProvenTransactionBuilder::new(
134 account.id(),
135 account.init_commitment(),
136 tx_outputs.account.commitment(),
137 ref_block_num,
138 ref_block_commitment,
139 tx_outputs.expiration_block_num,
140 proof,
141 )
142 .add_input_notes(input_notes)
143 .add_output_notes(output_notes);
144
145 let builder = match account.is_onchain() {
147 true => {
148 let account_update_details = if account.is_new() {
149 let mut account = account.clone();
150 account
151 .apply_delta(&account_delta)
152 .map_err(TransactionProverError::AccountDeltaApplyFailed)?;
153
154 AccountUpdateDetails::New(account)
155 } else {
156 AccountUpdateDetails::Delta(account_delta)
157 };
158
159 builder.account_update_details(account_update_details)
160 },
161 false => builder,
162 };
163
164 builder.build().map_err(TransactionProverError::ProvenTransactionBuildFailed)
165 }
166}