miden-precompiles-prover 0.33.0

Prover-side precompile implementations for the Miden VM deferred framework
Documentation
//! Per-chiplet test modules.
//!
//! Tests live alongside the code they exercise (one module per chiplet)
//! but are split out of the chiplet source files to keep the production
//! code easy to scan during audit.

mod aux_register;
mod batch_witness;
mod binding;
mod bus_balance;
mod byte_pair_lut;
mod chunk;
mod deferred_session;
mod deferred_state;
mod ec;
mod ec_add;
mod ec_dag;
mod ec_msm;
mod eval;
mod keccak;
mod keccak_node;
mod keccak_sponge;
mod poseidon2;
mod uint;
mod uint_add;
mod uint_dag;
mod uint_mul;
mod utils;
mod vm_uint;

use std::{vec, vec::Vec};

use miden_core::{
    Felt,
    deferred::DeferredRoot,
    field::QuadFelt,
    proof::{HashFunction, StarkProof},
    utils::RowMajorMatrix,
};
use miden_lifted_air::{BaseAir, LiftedAir, MultiAir, ProverStatement, ReductionError, Statement};
use miden_lifted_stark::check_constraints;
use miden_precompiles_verifier::{VerifyError, verify_deferred as verify_precompile};

use crate::{session::SessionTraces, stark_config::test_challenger};

pub(crate) type SessionProof = (StarkProof, DeferredRoot);

pub(crate) trait SessionTracesTestExt {
    fn prove(self) -> SessionProof;
}

impl SessionTracesTestExt for SessionTraces {
    fn prove(self) -> SessionProof {
        let public_root = self.public_root().as_array().into();
        let proof = self
            .prove_stark(HashFunction::Blake3_256)
            .expect("prove precompile session with default hash function");
        (proof, public_root)
    }
}

pub(crate) fn verify_deferred(proof: &SessionProof) -> Result<DeferredRoot, VerifyError> {
    verify_precompile(&proof.0, proof.1)?;
    Ok(proof.1)
}

/// A local-only [`MultiAir`] wrapper for per-chiplet
/// [`check_constraints`]: its `eval_external` emits no cross-AIR
/// assertion, so a single AIR's *local* constraints are checked without
/// the stack-level Σσ=0 closure (per-AIR σ ≠ 0). Mirrors the pre-0.26
/// per-AIR `check_constraints`.
struct LocalAir<A>(Vec<A>);

impl<A> MultiAir<Felt, QuadFelt> for LocalAir<A>
where
    A: LiftedAir<Felt, QuadFelt>,
{
    type Air = A;

    fn airs(&self) -> &[A] {
        &self.0
    }

    fn eval_external(
        &self,
        _challenges: &[QuadFelt],
        _air_inputs: &[Felt],
        _aux_inputs: &[Felt],
        _aux_values: &[&[QuadFelt]],
        _log_trace_heights: &[u8],
    ) -> Result<Vec<QuadFelt>, ReductionError> {
        Ok(Vec::new())
    }
}

/// Check one AIR's local constraints on `main` with explicit shared public
/// inputs (the eval chip's transcript root; dummy for chiplets that ignore
/// them — see [`check_local`]).
pub(crate) fn check_local_inputs<A>(air: A, main: &RowMajorMatrix<Felt>, air_inputs: Vec<Felt>)
where
    A: LiftedAir<Felt, QuadFelt>,
{
    let statement = Statement::new(LocalAir(vec![air]), air_inputs, Vec::new())
        .expect("local check statement inputs are valid");
    let ps = ProverStatement::new(statement, vec![main.clone()])
        .expect("local check trace shape is valid");
    check_constraints(&ps, test_challenger());
}

/// Check one AIR's local constraints on `main` with dummy public inputs
/// (zeros, sized to the shared `num_public_values`) — fine for every
/// chiplet except the eval chip, which should use [`check_local_inputs`]
/// with its real root.
pub(crate) fn check_local<A>(air: A, main: &RowMajorMatrix<Felt>)
where
    A: LiftedAir<Felt, QuadFelt>,
{
    let n = air.num_public_values();
    check_local_inputs(air, main, vec![Felt::ZERO; n]);
}

/// The per-AIR quotient degree used by the design-target smoke tests.
pub(crate) fn log_quotient_degree<A>(air: &A) -> u8
where
    A: LiftedAir<Felt, QuadFelt>,
{
    miden_lifted_stark::log_quotient_degree::<Felt, QuadFelt, A>(air)
}

/// The `[preprocessed ++ main]` matrix the lookup eval reads for a chiplet
/// with preprocessed columns — mirrors `logup::CombinedWindow` (constraint
/// side) and BytePairLut's prover-side combine. Returns `None` for chiplets
/// without preprocessed columns, so balance-check helpers pass `main`
/// straight to the prover-side fraction builder.
pub(crate) fn combined_lookup_main<A>(
    air: &A,
    main: &RowMajorMatrix<Felt>,
) -> Option<RowMajorMatrix<Felt>>
where
    A: BaseAir<Felt>,
{
    let pre = air.preprocessed_trace()?;
    let (pre_w, main_w) = (pre.width, main.width);
    let height = main.values.len() / main_w;
    let mut values = Vec::with_capacity(height * (pre_w + main_w));
    for r in 0..height {
        values.extend_from_slice(&pre.values[r * pre_w..(r + 1) * pre_w]);
        values.extend_from_slice(&main.values[r * main_w..(r + 1) * main_w]);
    }
    Some(RowMajorMatrix::new(values, pre_w + main_w))
}