1#![no_std]
2
3extern crate alloc;
4
5#[cfg(feature = "std")]
6extern crate std;
7
8use alloc::{boxed::Box, vec::Vec};
9
10use miden_air::{ProcessorAir, PublicInputs, config};
11use miden_core::{
12 Felt,
13 field::{QuadFelt, TwoAdicField},
14};
15use miden_crypto::stark::{
16 StarkConfig, air::VarLenPublicInputs, challenger::CanObserve, lmcs::Lmcs, proof::StarkProof,
17};
18use serde::de::DeserializeOwned;
19
20mod exports {
23 pub use miden_core::{
24 Word,
25 precompile::{
26 PrecompileTranscriptDigest, PrecompileTranscriptState, PrecompileVerificationError,
27 PrecompileVerifierRegistry,
28 },
29 program::{Kernel, ProgramInfo, StackInputs, StackOutputs},
30 proof::{ExecutionProof, HashFunction},
31 };
32 pub mod math {
33 pub use miden_core::Felt;
34 }
35}
36pub use exports::*;
37
38pub fn verify(
64 program_info: ProgramInfo,
65 stack_inputs: StackInputs,
66 stack_outputs: StackOutputs,
67 proof: ExecutionProof,
68) -> Result<u32, VerificationError> {
69 let (security_level, _commitment) = verify_with_precompiles(
70 program_info,
71 stack_inputs,
72 stack_outputs,
73 proof,
74 &PrecompileVerifierRegistry::new(),
75 )?;
76 Ok(security_level)
77}
78
79#[tracing::instrument("verify_program", skip_all)]
94pub fn verify_with_precompiles(
95 program_info: ProgramInfo,
96 stack_inputs: StackInputs,
97 stack_outputs: StackOutputs,
98 proof: ExecutionProof,
99 precompile_verifiers: &PrecompileVerifierRegistry,
100) -> Result<(u32, PrecompileTranscriptDigest), VerificationError> {
101 let security_level = proof.security_level();
102
103 let (hash_fn, proof_bytes, precompile_requests) = proof.into_parts();
104
105 let recomputed_transcript = precompile_verifiers
110 .requests_transcript(&precompile_requests)
111 .map_err(VerificationError::PrecompileVerificationError)?;
112 let pc_transcript_state = recomputed_transcript.state();
113
114 verify_stark(
116 program_info,
117 stack_inputs,
118 stack_outputs,
119 pc_transcript_state,
120 hash_fn,
121 proof_bytes,
122 )?;
123
124 let digest = recomputed_transcript.finalize();
126 Ok((security_level, digest))
127}
128
129fn verify_stark(
133 program_info: ProgramInfo,
134 stack_inputs: StackInputs,
135 stack_outputs: StackOutputs,
136 pc_transcript_state: PrecompileTranscriptState,
137 hash_fn: HashFunction,
138 proof_bytes: Vec<u8>,
139) -> Result<(), VerificationError> {
140 let program_hash = *program_info.program_hash();
141
142 let pub_inputs =
143 PublicInputs::new(program_info, stack_inputs, stack_outputs, pc_transcript_state);
144 let (public_values, kernel_felts) = pub_inputs.to_air_inputs();
145 let var_len_public_inputs: &[&[Felt]] = &[&kernel_felts];
146
147 let params = config::pcs_params();
148 match hash_fn {
149 HashFunction::Blake3_256 => {
150 let config = config::blake3_256_config(params);
151 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
152 },
153 HashFunction::Rpo256 => {
154 let config = config::rpo_config(params);
155 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
156 },
157 HashFunction::Rpx256 => {
158 let config = config::rpx_config(params);
159 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
160 },
161 HashFunction::Poseidon2 => {
162 let config = config::poseidon2_config(params);
163 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
164 },
165 HashFunction::Keccak => {
166 let config = config::keccak_config(params);
167 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
168 },
169 }
170 .map_err(|e| VerificationError::StarkVerificationError(program_hash, Box::new(e)))?;
171
172 Ok(())
173}
174
175#[derive(Debug, thiserror::Error)]
180pub enum VerificationError {
181 #[error("failed to verify STARK proof for program with hash {0}")]
182 StarkVerificationError(Word, #[source] Box<StarkVerificationError>),
183 #[error("failed to verify precompile calls")]
184 PrecompileVerificationError(#[source] PrecompileVerificationError),
185}
186
187#[derive(Debug, thiserror::Error)]
192pub enum StarkVerificationError {
193 #[error("failed to deserialize proof: {0}")]
194 Deserialization(#[from] bincode::Error),
195 #[error("log_trace_height {0} exceeds the two-adic order of the field")]
196 InvalidTraceHeight(u8),
197 #[error(transparent)]
198 Verifier(#[from] miden_crypto::stark::verifier::VerifierError),
199}
200
201fn verify_stark_proof<SC>(
206 config: &SC,
207 public_values: &[Felt],
208 var_len_public_inputs: VarLenPublicInputs<'_, Felt>,
209 proof_bytes: &[u8],
210) -> Result<(), StarkVerificationError>
211where
212 SC: StarkConfig<Felt, QuadFelt>,
213 <SC::Lmcs as Lmcs>::Commitment: DeserializeOwned,
214{
215 let (log_trace_height, proof): (u8, StarkProof<Felt, QuadFelt, SC>) =
219 bincode::deserialize(proof_bytes)?;
220
221 if log_trace_height as usize > Felt::TWO_ADICITY {
222 return Err(StarkVerificationError::InvalidTraceHeight(log_trace_height));
223 }
224
225 let mut challenger = config.challenger();
226 challenger.observe_slice(public_values);
227 miden_crypto::stark::verifier::verify_single(
234 config,
235 &ProcessorAir,
236 log_trace_height,
237 public_values,
238 var_len_public_inputs,
239 &proof,
240 challenger,
241 )?;
242 Ok(())
243}