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}