#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::{boxed::Box, vec::Vec};
use miden_air::{ProcessorAir, PublicInputs, config};
use miden_core::{
Felt,
field::{QuadFelt, TwoAdicField},
};
use miden_crypto::stark::{
StarkConfig, air::VarLenPublicInputs, challenger::CanObserve, lmcs::Lmcs, proof::StarkProof,
};
use serde::de::DeserializeOwned;
mod exports {
pub use miden_core::{
Word,
precompile::{
PrecompileTranscriptDigest, PrecompileTranscriptState, PrecompileVerificationError,
PrecompileVerifierRegistry,
},
program::{Kernel, ProgramInfo, StackInputs, StackOutputs},
proof::{ExecutionProof, HashFunction},
};
pub mod math {
pub use miden_core::Felt;
}
}
pub use exports::*;
pub fn verify(
program_info: ProgramInfo,
stack_inputs: StackInputs,
stack_outputs: StackOutputs,
proof: ExecutionProof,
) -> Result<u32, VerificationError> {
let (security_level, _commitment) = verify_with_precompiles(
program_info,
stack_inputs,
stack_outputs,
proof,
&PrecompileVerifierRegistry::new(),
)?;
Ok(security_level)
}
#[tracing::instrument("verify_program", skip_all)]
pub fn verify_with_precompiles(
program_info: ProgramInfo,
stack_inputs: StackInputs,
stack_outputs: StackOutputs,
proof: ExecutionProof,
precompile_verifiers: &PrecompileVerifierRegistry,
) -> Result<(u32, PrecompileTranscriptDigest), VerificationError> {
let security_level = proof.security_level();
let (hash_fn, proof_bytes, precompile_requests) = proof.into_parts();
let recomputed_transcript = precompile_verifiers
.requests_transcript(&precompile_requests)
.map_err(VerificationError::PrecompileVerificationError)?;
let pc_transcript_state = recomputed_transcript.state();
verify_stark(
program_info,
stack_inputs,
stack_outputs,
pc_transcript_state,
hash_fn,
proof_bytes,
)?;
let digest = recomputed_transcript.finalize();
Ok((security_level, digest))
}
fn verify_stark(
program_info: ProgramInfo,
stack_inputs: StackInputs,
stack_outputs: StackOutputs,
pc_transcript_state: PrecompileTranscriptState,
hash_fn: HashFunction,
proof_bytes: Vec<u8>,
) -> Result<(), VerificationError> {
let program_hash = *program_info.program_hash();
let pub_inputs =
PublicInputs::new(program_info, stack_inputs, stack_outputs, pc_transcript_state);
let (public_values, kernel_felts) = pub_inputs.to_air_inputs();
let var_len_public_inputs: &[&[Felt]] = &[&kernel_felts];
let params = config::pcs_params();
match hash_fn {
HashFunction::Blake3_256 => {
let config = config::blake3_256_config(params);
verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
},
HashFunction::Rpo256 => {
let config = config::rpo_config(params);
verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
},
HashFunction::Rpx256 => {
let config = config::rpx_config(params);
verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
},
HashFunction::Poseidon2 => {
let config = config::poseidon2_config(params);
verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
},
HashFunction::Keccak => {
let config = config::keccak_config(params);
verify_stark_proof(&config, &public_values, var_len_public_inputs, &proof_bytes)
},
}
.map_err(|e| VerificationError::StarkVerificationError(program_hash, Box::new(e)))?;
Ok(())
}
#[derive(Debug, thiserror::Error)]
pub enum VerificationError {
#[error("failed to verify STARK proof for program with hash {0}")]
StarkVerificationError(Word, #[source] Box<StarkVerificationError>),
#[error("failed to verify precompile calls")]
PrecompileVerificationError(#[source] PrecompileVerificationError),
}
#[derive(Debug, thiserror::Error)]
pub enum StarkVerificationError {
#[error("failed to deserialize proof: {0}")]
Deserialization(#[from] bincode::Error),
#[error("log_trace_height {0} exceeds the two-adic order of the field")]
InvalidTraceHeight(u8),
#[error(transparent)]
Verifier(#[from] miden_crypto::stark::verifier::VerifierError),
}
fn verify_stark_proof<SC>(
config: &SC,
public_values: &[Felt],
var_len_public_inputs: VarLenPublicInputs<'_, Felt>,
proof_bytes: &[u8],
) -> Result<(), StarkVerificationError>
where
SC: StarkConfig<Felt, QuadFelt>,
<SC::Lmcs as Lmcs>::Commitment: DeserializeOwned,
{
let (log_trace_height, proof): (u8, StarkProof<Felt, QuadFelt, SC>) =
bincode::deserialize(proof_bytes)?;
if log_trace_height as usize > Felt::TWO_ADICITY {
return Err(StarkVerificationError::InvalidTraceHeight(log_trace_height));
}
let mut challenger = config.challenger();
challenger.observe_slice(public_values);
miden_crypto::stark::verifier::verify_single(
config,
&ProcessorAir,
log_trace_height,
public_values,
var_len_public_inputs,
&proof,
challenger,
)?;
Ok(())
}