miden_tx/prover/
mod.rs

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// TRANSACTION PROVER TRAIT
22// ================================================================================================
23
24/// The [TransactionProver] trait defines the interface that transaction witness objects use to
25/// prove transactions and generate a [ProvenTransaction].
26#[maybe_async_trait]
27pub trait TransactionProver {
28    /// Proves the provided transaction and returns a [ProvenTransaction].
29    ///
30    /// # Errors
31    /// - If the input note data in the transaction witness is corrupt.
32    /// - If the transaction program cannot be proven.
33    /// - If the transaction result is corrupt.
34    #[maybe_async]
35    fn prove(
36        &self,
37        tx_witness: TransactionWitness,
38    ) -> Result<ProvenTransaction, TransactionProverError>;
39}
40
41// LOCAL TRANSACTION PROVER
42// ------------------------------------------------------------------------------------------------
43
44/// Local Transaction prover is a stateless component which is responsible for proving transactions.
45///
46/// Local Transaction Prover implements the [TransactionProver] trait.
47pub struct LocalTransactionProver {
48    mast_store: Arc<TransactionMastStore>,
49    proof_options: ProvingOptions,
50}
51
52impl LocalTransactionProver {
53    /// Creates a new [LocalTransactionProver] instance.
54    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        // execute and prove
86        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        // load the store with account/note/tx_script MASTs
92        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        // For the prover, we assume that the transaction witness was successfully executed and so
110        // there is no need to provide the actual source manager, as it is only used to improve
111        // error quality. So we simply pass an empty one.
112        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        // extract transaction outputs and process transaction data
123        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        // erase private note information (convert private full notes to just headers)
131        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        // If the account is on-chain, add the update details.
146        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}