miden_tx/prover/
mod.rs

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
25// LOCAL TRANSACTION PROVER
26// ------------------------------------------------------------------------------------------------
27
28/// Local Transaction prover is a stateless component which is responsible for proving transactions.
29pub struct LocalTransactionProver {
30    mast_store: Arc<TransactionMastStore>,
31    proof_options: ProvingOptions,
32}
33
34impl LocalTransactionProver {
35    /// Creates a new [LocalTransactionProver] instance.
36    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        // execute and prove
69        let (stack_inputs, advice_inputs) =
70            TransactionKernel::prepare_inputs(&tx_inputs, &tx_args, Some(advice_witness))
71                .map_err(TransactionProverError::ConflictingAdviceMapEntry)?;
72
73        // load the store with account/note/tx_script MASTs
74        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        // Extract transaction outputs and process transaction data.
113        // Note that the account delta does not contain the removed transaction fee, so it is the
114        // "pre-fee" delta of the transaction.
115        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        // erase private note information (convert private full notes to just headers)
121        let output_notes: Vec<_> = tx_outputs.output_notes.iter().map(OutputNote::shrink).collect();
122
123        // Compute the commitment of the pre-fee delta, which goes into the proven transaction,
124        // since it is the output of the transaction and so is needed for proof verification.
125        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        // The full transaction delta is the pre fee delta with the fee asset removed.
142        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        // If the account is on-chain, add the update details.
149        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}