miden_verifier/
lib.rs

1#![no_std]
2
3extern crate alloc;
4
5#[cfg(feature = "std")]
6extern crate std;
7
8use alloc::vec;
9
10use miden_air::{HashFunction, ProcessorAir, ProvingOptions, PublicInputs};
11use miden_core::crypto::{
12    hash::{Blake3_192, Blake3_256, Poseidon2, Rpo256, Rpx256},
13    random::{RpoRandomCoin, RpxRandomCoin, WinterRandomCoin},
14};
15use winter_verifier::{crypto::MerkleTree, verify as verify_proof};
16
17// EXPORTS
18// ================================================================================================
19mod exports {
20    pub use miden_core::{
21        Kernel, ProgramInfo, StackInputs, StackOutputs, Word,
22        precompile::{
23            PrecompileError, PrecompileTranscriptDigest, PrecompileVerificationError,
24            PrecompileVerifierRegistry,
25        },
26    };
27    pub use winter_verifier::{AcceptableOptions, VerifierError};
28    pub mod math {
29        pub use miden_core::{Felt, FieldElement, StarkField};
30    }
31    pub use miden_air::ExecutionProof;
32}
33pub use exports::*;
34
35// VERIFIER
36// ================================================================================================
37
38/// Returns the security level of the proof if the specified program was executed correctly against
39/// the specified inputs and outputs.
40///
41/// Specifically, verifies that if a program with the specified `program_hash` is executed against
42/// the provided `stack_inputs` and some secret inputs, the result is equal to the `stack_outputs`.
43///
44/// Stack inputs are expected to be ordered as if they would be pushed onto the stack one by one.
45/// Thus, their expected order on the stack will be the reverse of the order in which they are
46/// provided, and the last value in the `stack_inputs` slice is expected to be the value at the top
47/// of the stack.
48///
49/// Stack outputs are expected to be ordered as if they would be popped off the stack one by one.
50/// Thus, the value at the top of the stack is expected to be in the first position of the
51/// `stack_outputs` slice, and the order of the rest of the output elements will also match the
52/// order on the stack. This is the reverse of the order of the `stack_inputs` slice.
53///
54/// The verifier accepts proofs generated using a parameter set defined in [ProvingOptions].
55/// Specifically, parameter sets targeting the following are accepted:
56/// - 96-bit security level, non-recursive context (BLAKE3 hash function).
57/// - 96-bit security level, recursive context (BLAKE3 hash function).
58/// - 128-bit security level, non-recursive context (RPO hash function).
59/// - 128-bit security level, recursive context (RPO hash function).
60///
61/// # Errors
62/// Returns an error if:
63/// - The provided proof does not prove a correct execution of the program.
64/// - The protocol parameters used to generate the proof are not in the set of acceptable
65///   parameters.
66/// - The proof contains one or more precompile requests. When precompile requests are present, use
67///   [`verify_with_precompiles`] instead with an appropriate [`PrecompileVerifierRegistry`] to
68///   verify the precompile computations.
69pub fn verify(
70    program_info: ProgramInfo,
71    stack_inputs: StackInputs,
72    stack_outputs: StackOutputs,
73    proof: ExecutionProof,
74) -> Result<u32, VerificationError> {
75    let (security_level, _commitment) = verify_with_precompiles(
76        program_info,
77        stack_inputs,
78        stack_outputs,
79        proof,
80        &PrecompileVerifierRegistry::new(),
81    )?;
82    Ok(security_level)
83}
84
85/// Identical to [`verify`], with additional verification of any precompile requests made during the
86/// VM execution. The resulting aggregated precompile commitment is returned, which can be compared
87/// against the commitment computed by the VM.
88///
89/// # Returns
90/// Returns a tuple `(security_level, aggregated_commitment)` where:
91/// - `security_level`: The security level (in bits) of the verified proof
92/// - `aggregated_commitment`: A [`Word`] containing the final aggregated commitment to all
93///   precompile requests, computed by recomputing and recording each precompile commitment in a
94///   transcript. This value is the finalized digest of the recomputed precompile transcript.
95///
96/// # Errors
97/// Returns any error produced by [`verify`], as well as any errors resulting from precompile
98/// verification.
99#[tracing::instrument("verify_program", skip_all)]
100pub fn verify_with_precompiles(
101    program_info: ProgramInfo,
102    stack_inputs: StackInputs,
103    stack_outputs: StackOutputs,
104    proof: ExecutionProof,
105    precompile_verifiers: &PrecompileVerifierRegistry,
106) -> Result<(u32, PrecompileTranscriptDigest), VerificationError> {
107    // get security level of the proof
108    let security_level = proof.security_level();
109    let program_hash = *program_info.program_hash();
110
111    let (hash_fn, proof, precompile_requests) = proof.into_parts();
112
113    // recompute the precompile transcript by verifying all precompile requests and recording the
114    // commitments.
115    // if no verifiers were provided (e.g. when this function was called from `verify()`),
116    // but the proof contained requests anyway, returns a `NoVerifierFound` error.
117    let recomputed_transcript = precompile_verifiers
118        .requests_transcript(&precompile_requests)
119        .map_err(VerificationError::PrecompileVerificationError)?;
120
121    // build public inputs, explicitly passing the recomputed precompile transcript state
122    let pub_inputs =
123        PublicInputs::new(program_info, stack_inputs, stack_outputs, recomputed_transcript.state());
124
125    match hash_fn {
126        HashFunction::Blake3_192 => {
127            let opts = AcceptableOptions::OptionSet(vec![ProvingOptions::REGULAR_96_BITS]);
128            verify_proof::<ProcessorAir, Blake3_192, WinterRandomCoin<_>, MerkleTree<_>>(
129                proof, pub_inputs, &opts,
130            )
131        },
132        HashFunction::Blake3_256 => {
133            let opts = AcceptableOptions::OptionSet(vec![ProvingOptions::REGULAR_128_BITS]);
134            verify_proof::<ProcessorAir, Blake3_256, WinterRandomCoin<_>, MerkleTree<_>>(
135                proof, pub_inputs, &opts,
136            )
137        },
138        HashFunction::Rpo256 => {
139            let opts = AcceptableOptions::OptionSet(vec![
140                ProvingOptions::RECURSIVE_96_BITS,
141                ProvingOptions::RECURSIVE_128_BITS,
142            ]);
143            verify_proof::<ProcessorAir, Rpo256, RpoRandomCoin, MerkleTree<_>>(
144                proof, pub_inputs, &opts,
145            )
146        },
147        HashFunction::Rpx256 => {
148            let opts = AcceptableOptions::OptionSet(vec![
149                ProvingOptions::RECURSIVE_96_BITS,
150                ProvingOptions::RECURSIVE_128_BITS,
151            ]);
152            verify_proof::<ProcessorAir, Rpx256, RpxRandomCoin, MerkleTree<_>>(
153                proof, pub_inputs, &opts,
154            )
155        },
156        HashFunction::Poseidon2 => {
157            let opts = AcceptableOptions::OptionSet(vec![
158                ProvingOptions::RECURSIVE_96_BITS,
159                ProvingOptions::REGULAR_128_BITS,
160            ]);
161            verify_proof::<ProcessorAir, Poseidon2, WinterRandomCoin<_>, MerkleTree<_>>(
162                proof, pub_inputs, &opts,
163            )
164        },
165    }
166    .map_err(|source| VerificationError::ProgramVerificationError(program_hash, source))?;
167
168    // finalize transcript to return the digest
169    let digest = recomputed_transcript.finalize();
170    Ok((security_level, digest))
171}
172
173// ERRORS
174// ================================================================================================
175
176/// Errors that can occur during proof verification.
177#[derive(Debug, thiserror::Error)]
178pub enum VerificationError {
179    #[error("failed to verify proof for program with hash {0}")]
180    ProgramVerificationError(Word, #[source] VerifierError),
181    #[error("the input {0} is not a valid field element")]
182    InputNotFieldElement(u64),
183    #[error("the output {0} is not a valid field element")]
184    OutputNotFieldElement(u64),
185    #[error("failed to verify precompile calls")]
186    PrecompileVerificationError(#[source] PrecompileVerificationError),
187}