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::{Felt, WORD_SIZE, field::QuadFelt};
12use miden_crypto::stark::{
13 StarkConfig, air::VarLenPublicInputs, challenger::CanObserve, lmcs::Lmcs, proof::StarkProof,
14};
15use serde::de::DeserializeOwned;
16
17mod exports {
20 pub use miden_core::{
21 Word,
22 precompile::{
23 PrecompileTranscriptDigest, PrecompileTranscriptState, PrecompileVerificationError,
24 PrecompileVerifierRegistry,
25 },
26 program::{Kernel, ProgramInfo, StackInputs, StackOutputs},
27 proof::{ExecutionProof, HashFunction},
28 };
29 pub mod math {
30 pub use miden_core::Felt;
31 }
32}
33pub use exports::*;
34
35pub fn verify(
61 program_info: ProgramInfo,
62 stack_inputs: StackInputs,
63 stack_outputs: StackOutputs,
64 proof: ExecutionProof,
65) -> Result<u32, VerificationError> {
66 let (security_level, _commitment) = verify_with_precompiles(
67 program_info,
68 stack_inputs,
69 stack_outputs,
70 proof,
71 &PrecompileVerifierRegistry::new(),
72 )?;
73 Ok(security_level)
74}
75
76#[tracing::instrument("verify_program", skip_all)]
91pub fn verify_with_precompiles(
92 program_info: ProgramInfo,
93 stack_inputs: StackInputs,
94 stack_outputs: StackOutputs,
95 proof: ExecutionProof,
96 precompile_verifiers: &PrecompileVerifierRegistry,
97) -> Result<(u32, PrecompileTranscriptDigest), VerificationError> {
98 let security_level = proof.security_level();
99
100 let (hash_fn, proof_bytes, precompile_requests) = proof.into_parts();
101
102 let recomputed_transcript = precompile_verifiers
107 .requests_transcript(&precompile_requests)
108 .map_err(VerificationError::PrecompileVerificationError)?;
109 let pc_transcript_state = recomputed_transcript.state();
110
111 verify_stark(
113 program_info,
114 stack_inputs,
115 stack_outputs,
116 pc_transcript_state,
117 hash_fn,
118 proof_bytes,
119 )?;
120
121 let digest = recomputed_transcript.finalize();
123 Ok((security_level, digest))
124}
125
126fn verify_stark(
130 program_info: ProgramInfo,
131 stack_inputs: StackInputs,
132 stack_outputs: StackOutputs,
133 pc_transcript_state: PrecompileTranscriptState,
134 hash_fn: HashFunction,
135 proof_bytes: Vec<u8>,
136) -> Result<(), VerificationError> {
137 let program_hash = *program_info.program_hash();
138
139 let pub_inputs =
140 PublicInputs::new(program_info, stack_inputs, stack_outputs, pc_transcript_state);
141 let (public_values, kernel_felts) = pub_inputs.to_air_inputs();
142 let var_len_public_inputs: &[&[Felt]] = &[&kernel_felts];
143
144 let params = config::pcs_params();
145 match hash_fn {
146 HashFunction::Blake3_256 => {
147 let config = config::blake3_256_config(params);
148 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
149 },
150 HashFunction::Rpo256 => {
151 let config = config::rpo_config(params);
152 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
153 },
154 HashFunction::Rpx256 => {
155 let config = config::rpx_config(params);
156 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
157 },
158 HashFunction::Poseidon2 => {
159 let config = config::poseidon2_config(params);
160 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
161 },
162 HashFunction::Keccak => {
163 let config = config::keccak_config(params);
164 verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
165 },
166 }
167 .map_err(|e| VerificationError::StarkVerificationError(program_hash, Box::new(e)))?;
168
169 Ok(())
170}
171
172#[derive(Debug, thiserror::Error)]
177pub enum VerificationError {
178 #[error("failed to verify STARK proof for program with hash {0}")]
179 StarkVerificationError(Word, #[source] Box<StarkVerificationError>),
180 #[error("failed to verify precompile calls")]
181 PrecompileVerificationError(#[source] PrecompileVerificationError),
182}
183
184#[derive(Debug, thiserror::Error)]
189pub enum StarkVerificationError {
190 #[error("failed to deserialize proof: {0}")]
191 Deserialization(#[from] bincode::Error),
192 #[error("log_trace_height {0} exceeds the two-adic order of the field")]
193 InvalidTraceHeight(u8),
194 #[error(transparent)]
195 Verifier(#[from] miden_crypto::stark::verifier::VerifierError),
196}
197
198fn verify_stark_proof<SC>(
203 config: &SC,
204 public_values: &[Felt],
205 var_len_public_inputs: VarLenPublicInputs<'_, Felt>,
206 proof_bytes: &[u8],
207) -> Result<(), StarkVerificationError>
208where
209 SC: StarkConfig<Felt, QuadFelt>,
210 <SC::Lmcs as Lmcs>::Commitment: DeserializeOwned,
211{
212 let proof: StarkProof<Felt, QuadFelt, SC> = bincode::deserialize(proof_bytes)?;
214
215 let mut challenger = config.challenger();
216 config::observe_protocol_params(&mut challenger);
217 challenger.observe_slice(public_values);
218 config::observe_var_len_public_inputs(&mut challenger, var_len_public_inputs, &[WORD_SIZE]);
219 miden_crypto::stark::verifier::verify_single(
220 config,
221 &ProcessorAir,
222 public_values,
223 var_len_public_inputs,
224 &proof,
225 challenger,
226 )?;
227 Ok(())
228}