Skip to main content

miden_verifier/
lib.rs

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