samaharam 0.2.0

Scalable heterogeneous zero-knowledge proof aggregation for EVM chains
Documentation
//! Core trait for external proof adaptation.
//!
//! # Overview
//!
//! This module defines the [`ExternalProof`] trait for converting proofs from external
//! systems (snarkjs, gnark, jf-plonk) into samaharam's [`AccumulatorInstance`] format.
//!
//! # Groth16 vs PLONK Compatibility (Expert Panel Note)
//!
//! Per Dan Boneh and Eli Ben-Sasson's review, there are important mathematical
//! differences between proof systems:
//!
//! ## Groth16 (snarkjs/circom)
//!
//! Groth16 verification uses a pairing equation:
//! ```text
//! e(A, B) = e(α, β) · e(Σ aᵢLᵢ, γ) · e(C, δ)
//! ```
//! The proof consists of points (A, B, C) with no polynomial commitment structure.
//!
//! ## PLONK (gnark, jf-plonk)
//!
//! PLONK verification uses polynomial commitments with KZG openings:
//! ```text
//! Commitments: [a]₁, [b]₁, [c]₁, [z]₁, [t]₁
//! Openings: Quotient polynomials at challenge point ζ
//! ```
//! Each opening is a KZG proof: (commitment, point, evaluation, quotient).
//!
//! ## Adaptation Strategy
//!
//! The [`to_accumulator_instances`] method extracts KZG-like opening instances:
//!
//! | Proof System | Strategy |
//! |--------------|----------|
//! | **Groth16** | Extract A and C as commitments, use synthetic evaluations |
//! | **PLONK** | Direct extraction of wire/quotient polynomial openings |
//!
//! ### Security Consideration
//!
//! **Homogeneous batching** (same proof type) is more efficient and secure.
//! **Heterogeneous batching** (mixing Groth16 and PLONK) requires careful
//! handling to ensure the random linear combination doesn't introduce
//! soundness issues.
//!
//! For production use:
//! 1. Prefer batching proofs of the same type together
//! 2. Use separate accumulator instances for different proof types
//! 3. Verify the mathematical soundness of cross-type aggregation
//!
//! # Example
//!
//! ```rust,ignore
//! use samaharam::adapters::{ExternalProof, SnarkjsProof, GnarkPlonkProof};
//!
//! // Homogeneous batching (recommended)
//! let mut plonk_accumulator = ProofAccumulator::new("plonk");
//! for proof in gnark_proofs {
//!     let instances = proof.to_accumulator_instances()?;
//!     for instance in instances {
//!         plonk_accumulator.add(instance);
//!     }
//! }
//!
//! // Heterogeneous batching (use with caution)
//! let mut mixed_accumulator = ProofAccumulator::new("mixed");
//! mixed_accumulator.add(groth16_proof.to_accumulator_instances()?[0].clone());
//! mixed_accumulator.add(plonk_proof.to_accumulator_instances()?[0].clone());
//! ```

use crate::crypto::AccumulatorInstance;
use crate::traits::PairingEngine;
use std::fmt;

/// Error type for adapter operations.
#[derive(Debug, Clone)]
pub enum AdapterError {
    /// Invalid proof format or structure.
    InvalidFormat(String),
    /// Point not on curve.
    InvalidPoint(String),
    /// Unsupported proof configuration.
    Unsupported(String),
    /// Parsing error.
    ParseError(String),
}

impl fmt::Display for AdapterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AdapterError::InvalidFormat(msg) => write!(f, "Invalid format: {}", msg),
            AdapterError::InvalidPoint(msg) => write!(f, "Invalid point: {}", msg),
            AdapterError::Unsupported(msg) => write!(f, "Unsupported: {}", msg),
            AdapterError::ParseError(msg) => write!(f, "Parse error: {}", msg),
        }
    }
}

impl std::error::Error for AdapterError {}

/// Metadata about an external proof.
#[derive(Debug, Clone)]
pub struct ProofMetadata {
    /// Name of the proof system (e.g., "snarkjs", "gnark").
    pub system: &'static str,
    /// Proof type (e.g., "groth16", "plonk").
    pub proof_type: &'static str,
    /// Curve used.
    pub curve: &'static str,
    /// Number of public inputs.
    pub num_public_inputs: usize,
}

/// Trait for converting external proofs to samaharam's aggregation format.
///
/// Implement this trait for any external proof system to enable aggregation.
///
/// # Example
///
/// ```rust,ignore
/// use samaharam::adapters::{ExternalProof, SnarkjsProof};
///
/// let proof = SnarkjsProof::from_json(json_data)?;
/// let instances = proof.to_accumulator_instances()?;
/// for instance in instances {
///     accumulator.add(instance);
/// }
/// ```
pub trait ExternalProof<E: PairingEngine>: Send + Sync {
    /// Convert to accumulator instances for aggregation.
    ///
    /// Returns one or more `AccumulatorInstance` that can be added to a
    /// `ProofAccumulator` for batched verification.
    fn to_accumulator_instances(&self) -> Result<Vec<AccumulatorInstance<E>>, AdapterError>;

    /// Get the public inputs from this proof.
    fn public_inputs(&self) -> &[E::Fr];

    /// Get metadata about this proof.
    fn metadata(&self) -> ProofMetadata;

    /// Validate the proof format (not cryptographic verification).
    ///
    /// Checks that points are on curve, lengths are correct, etc.
    fn validate_format(&self) -> Result<(), AdapterError>;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adapter_error_displays() {
        let err = AdapterError::InvalidFormat("bad point".to_string());
        assert!(err.to_string().contains("Invalid format"));
    }
}