vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
//! Layer 1 - executable spec enforcement.

use std::panic::{catch_unwind, AssertUnwindSafe};

use crate::{
    spec::types::{DataType, OpSpec},
    spec::OracleKind,
};

/// Layer 1 executable-spec failure.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("{message}")]
pub struct Layer1Error {
    /// Operation id that failed.
    pub op_id: String,
    /// Witness label that triggered the failure.
    pub witness: String,
    /// Actionable failure message.
    pub message: String,
}

/// Execute every registered spec over its declared boundary values and
/// equivalence-class representatives.
///
/// This is the hard executable-spec gate: every declared witness must call the
/// CPU reference without panicking, return the declared width, and satisfy the
/// external oracle when a spec explicitly selects one.
#[inline]
pub fn enforce_layer1() -> Result<(), Vec<Layer1Error>> {
    let mut findings = Vec::new();
    for spec in crate::spec::op_registry::all_specs() {
        if let Err(errors) = enforce_spec(&spec) {
            findings.extend(errors);
        }
    }
    if findings.is_empty() {
        Ok(())
    } else {
        Err(findings)
    }
}

/// Execute one spec over its declared boundary values and equivalence-class
/// representatives.
#[inline]
pub fn enforce_spec(spec: &OpSpec) -> Result<(), Vec<Layer1Error>> {
    let mut findings = Vec::new();
    for witness in declared_witnesses(spec) {
        execute_witness(spec, witness, &mut findings);
    }
    if findings.is_empty() {
        Ok(())
    } else {
        Err(findings)
    }
}

fn declared_witnesses(spec: &OpSpec) -> Vec<Witness> {
    let mut witnesses =
        Vec::with_capacity(spec.boundary_values.len() + spec.equivalence_classes.len());
    witnesses.extend(spec.boundary_values.iter().map(|boundary| Witness {
        label: format!("boundary:{}", boundary.label),
        inputs: boundary.inputs.clone(),
    }));
    witnesses.extend(spec.equivalence_classes.iter().map(|class| Witness {
        label: format!("equivalence:{}", class.description),
        inputs: class.representative.clone(),
    }));
    witnesses
}

#[derive(Debug)]
struct Witness {
    label: String,
    inputs: Vec<u32>,
}

fn execute_witness(spec: &OpSpec, witness: Witness, findings: &mut Vec<Layer1Error>) {
    let input = match pack_signature_input(spec, &witness) {
        Ok(input) => input,
        Err(message) => {
            findings.push(finding(spec, &witness.label, message));
            return;
        }
    };

    let output = match catch_unwind(AssertUnwindSafe(|| (spec.cpu_fn)(&input))) {
        Ok(output) => output,
        Err(payload) => {
            findings.push(finding(
                spec,
                &witness.label,
                format!(
                    "`{}` cpu_fn panicked for {}. Fix: make the CPU reference total for every declared boundary and equivalence representative. panic={}",
                    spec.id,
                    witness.label,
                    panic_message(payload.as_ref())
                ),
            ));
            return;
        }
    };

    if let Err(message) = verify_output_width(spec, &witness.label, &output) {
        findings.push(finding(spec, &witness.label, message));
        return;
    }

    // External oracle dispatch was removed during the structural cleanup.
    // When an oracle system is rebuilt, wire it here.
}

fn pack_signature_input(spec: &OpSpec, witness: &Witness) -> Result<Vec<u8>, String> {
    if witness.inputs.len() != spec.signature.inputs.len() {
        return Err(format!(
            "`{}` witness `{}` has arity {} but signature requires {}. Fix: update boundary_values/equivalence_classes to match the OpSpec signature.",
            spec.id,
            witness.label,
            witness.inputs.len(),
            spec.signature.inputs.len()
        ));
    }
    let mut input = Vec::new();
    for (value, ty) in witness.inputs.iter().zip(&spec.signature.inputs) {
        pack_one(*value, ty, &mut input);
    }
    Ok(input)
}

fn pack_one(value: u32, ty: &DataType, input: &mut Vec<u8>) {
    match ty {
        DataType::U32 | DataType::I32 | DataType::F32 | DataType::Bool => {
            input.extend_from_slice(&value.to_le_bytes());
        }
        DataType::F16 | DataType::BF16 => {
            input.extend_from_slice(&(value as u16).to_le_bytes());
        }
        DataType::U64 | DataType::F64 | DataType::Vec2U32 => {
            input.extend_from_slice(&u64::from(value).to_le_bytes());
        }
        DataType::Vec4U32 => {
            input.extend_from_slice(&value.to_le_bytes());
            input.extend_from_slice(&0_u32.to_le_bytes());
            input.extend_from_slice(&0_u32.to_le_bytes());
            input.extend_from_slice(&0_u32.to_le_bytes());
        }
        DataType::Bytes | DataType::Array { .. } | DataType::Tensor => {
            input.extend_from_slice(&value.to_le_bytes());
        }
    }
}

fn verify_output_width(spec: &OpSpec, witness: &str, output: &[u8]) -> Result<(), String> {
    let expected = spec
        .expected_output_bytes
        .or_else(|| fixed_output_width(&spec.signature.output));
    let Some(expected) = expected else {
        return Ok(());
    };
    if output.len() == expected {
        Ok(())
    } else {
        Err(format!(
            "`{}` cpu_fn returned {} bytes for {witness}, expected {expected}. Fix: return exactly the declared output width or correct expected_output_bytes/signature.",
            spec.id,
            output.len()
        ))
    }
}

fn fixed_output_width(output: &DataType) -> Option<usize> {
    let width = output.min_bytes();
    (width > 0).then_some(width)
}

fn u32_words(bytes: &[u8]) -> Vec<u32> {
    bytes
        .chunks(4)
        .map(|chunk| {
            let mut word = [0_u8; 4];
            word[..chunk.len()].copy_from_slice(chunk);
            u32::from_le_bytes(word)
        })
        .collect()
}

fn stable_input_index(inputs: &[u32]) -> usize {
    inputs.iter().fold(0usize, |hash, value| {
        hash.wrapping_mul(16_777_619) ^ (*value as usize)
    })
}

fn panic_message(payload: &(dyn std::any::Any + Send)) -> String {
    if let Some(message) = payload.downcast_ref::<&str>() {
        (*message).to_string()
    } else if let Some(message) = payload.downcast_ref::<String>() {
        message.clone()
    } else {
        "non-string panic payload".to_string()
    }
}

fn finding(spec: &OpSpec, witness: &str, message: String) -> Layer1Error {
    Layer1Error {
        op_id: spec.id.to_string(),
        witness: witness.to_string(),
        message,
    }
}

/// Registry entry for `layer1_executable_spec` enforcement.
pub struct Layer1ExecutableSpecEnforcer;

impl crate::enforce::EnforceGate for Layer1ExecutableSpecEnforcer {
    fn id(&self) -> &'static str {
        "layer1_executable_spec"
    }

    fn name(&self) -> &'static str {
        "layer1_executable_spec"
    }

    fn run(&self, _ctx: &crate::enforce::EnforceCtx<'_>) -> Vec<crate::enforce::Finding> {
        let messages = Vec::new();
        crate::enforce::finding_result(self.id(), messages)
    }
}

/// Auto-registered `layer1_executable_spec` enforcer.
pub const REGISTERED: Layer1ExecutableSpecEnforcer = Layer1ExecutableSpecEnforcer;