Skip to main content

miden_protocol/transaction/
verifier.rs

1use miden_verifier::{
2    ExecutionClaim,
3    PrecompileStatus,
4    VerificationError,
5    VerificationOutcome,
6    Verifier,
7};
8
9use crate::errors::TransactionVerifierError;
10use crate::transaction::{ProvenTransaction, TransactionKernel};
11use crate::vm::ProgramInfo;
12
13// TRANSACTION VERIFIER
14// ================================================================================================
15
16/// The [TransactionVerifier] is used to verify  [ProvenTransaction]s.
17///
18/// The [TransactionVerifier] contains a [ProgramInfo] object which is associated with the
19/// transaction kernel program.  The `proof_security_level` specifies the minimum security
20/// level that the transaction proof must have in order to be considered valid.
21pub struct TransactionVerifier {
22    tx_program_info: ProgramInfo,
23    proof_security_level: u32,
24}
25
26impl TransactionVerifier {
27    /// Returns a new [TransactionVerifier] instantiated with the specified security level.
28    pub fn new(proof_security_level: u32) -> Self {
29        let tx_program_info = TransactionKernel::program_info();
30        Self { tx_program_info, proof_security_level }
31    }
32
33    /// Verifies the provided [`ProvenTransaction`] against the transaction kernel and returns its
34    /// verification outcome.
35    ///
36    /// A verified transaction may still have an outstanding precompile obligation. Callers must
37    /// inspect the returned [`VerificationOutcome`] and handle that obligation if present.
38    ///
39    /// # Errors
40    /// Returns an error if:
41    /// - The proof contains settled precompile work.
42    /// - Transaction verification fails.
43    /// - A deferred precompile witness is invalid or does not match the VM proof.
44    /// - The security level of the verified proof is insufficient.
45    pub fn verify(
46        &self,
47        transaction: &ProvenTransaction,
48    ) -> Result<VerificationOutcome, TransactionVerifierError> {
49        if matches!(transaction.proof().precompile(), PrecompileStatus::Proven(_)) {
50            return Err(TransactionVerifierError::TransactionProofContainsPrecompiles);
51        }
52
53        // build stack inputs and outputs
54        let stack_inputs = TransactionKernel::build_input_stack(
55            transaction.account_id(),
56            transaction.account_update().initial_state_commitment(),
57            transaction.input_notes().commitment(),
58            transaction.ref_block_commitment(),
59            transaction.ref_block_num(),
60        );
61        let stack_outputs = TransactionKernel::build_output_stack(
62            transaction.account_update().final_state_commitment(),
63            transaction.account_update().account_patch_commitment(),
64            transaction.output_notes().commitment(),
65            transaction.expiration_block_num(),
66        );
67
68        // verify transaction proof
69        let claim = ExecutionClaim::from_program_info(
70            self.tx_program_info.clone(),
71            stack_inputs,
72            stack_outputs,
73        );
74        let outcome = Verifier::new()
75            .with_min_conjectured_security_level_per_stark(self.proof_security_level)
76            .verify(&claim, transaction.proof())
77            .map_err(|error| match error {
78                VerificationError::InsufficientSecurityLevel { actual, required } => {
79                    TransactionVerifierError::InsufficientProofSecurityLevel {
80                        actual,
81                        expected_minimum: required,
82                    }
83                },
84                error => TransactionVerifierError::TransactionVerificationFailed(error),
85            })?;
86
87        Ok(outcome)
88    }
89}